I have the following json that I want to deserialize in order to insert 
data into 3 tables.

{
   "id":"game_id",
   "rated":true,
   "variant":"standard",
   "speed":"fast",
   "perf":"fast",
   "createdAt": 1234,
   "lastMoveAt":1234,
   "status":"resign",
   "players":{
      "white":{
         "user":{
            "name":"player1",
            "id":"player1"
         },
         "rating":1000,
         "ratingDiff":-5
      },
      "black":{
         "user":{
            "name":"player2",
            "id":"player2"
         },
         "rating":1001,
         "ratingDiff":5
      }
   },
   "winner":"black",
   "opening":{
      "eco":"Z99",
      "name":"my opening",
      "ply":2
   },
   "moves":"1, 2, 3, 4",
   "clock":{
      "initial":60,
      "increment":0,
      "totalTime":60
   }
}

Here are my models :

class User(models.Model):

    class Meta:
        ordering = ["id"]

    id = models.CharField(primary_key=True, max_length=100)
    name = models.CharField(max_length=100)

    def __repr__(self):
        return f"User(id={self.id!r}, name={self.name!r})"


class Opening(models.Model):

    class Meta:
        ordering = ["eco"]
        constraints = [
            models.UniqueConstraint(
                fields=["name", "eco"], name="eco_name"
            )
        ]

    name = models.CharField(max_length=200)
    eco = models.CharField(max_length=3)

    def __repr__(self):
        return f"User(name={self.name!r}, eco={self.eco!r})"


class Game(models.Model):

    class Meta:
        ordering = ["last_move_at"]

    id = models.CharField(primary_key=True, max_length=15)
    rated = models.BooleanField(default=True)
    variant = models.CharField(max_length=200)
    speed = models.CharField(max_length=50)
    perf = models.CharField(max_length=50)
    created_at = models.IntegerField()
    last_move_at = models.IntegerField()
    status = models.CharField(max_length=50)
    winner = models.CharField(max_length=100)
    moves = models.TextField(max_length=1000)
    white_rating = models.IntegerField()
    white_rating_diff = models.IntegerField()
    white = models.ForeignKey(User, related_name="white_id", 
on_delete=models.CASCADE)
    black_rating = models.IntegerField()
    black_rating_diff = models.IntegerField()
    black = models.ForeignKey(User, related_name="black_id", 
on_delete=models.CASCADE)
    opening = models.ForeignKey(Opening, related_name="eco_name", 
on_delete=models.CASCADE)
    initial_time = models.IntegerField(null=True)
    increment = models.IntegerField(null=True)
    total_time = models.CharField(max_length=200)


Here are my serializers :

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ["id", "name"]

class OpeningSerializer(serializers.ModelSerializer):
    class Meta:
        model = Opening
        fields = ["name", "eco"]

    def create(self, validated_data):
        return Opening.objects.get_or_create(**validated_data)[0]


class GameSerializer(serializers.ModelSerializer):

    class Meta:
        model = Game
        fields = ['id', 'rated', 'variant', 'speed', 'perf', 'created_at', 
'last_move_at',
                  'status', 'winner', 'moves', 'white_rating', 
'white_rating_diff',
                  'white', 'black_rating', 'black_rating_diff', 'black', 
"opening",
                  'initial_time', 'increment', 'total_time']
      
  def to_internal_value(self, data):
    internal_value = {"opening": data.pop("opening")}
    del data["pgn"]
    white_data = data.get("players")["white"]
    internal_value["white"] = white_data["user"]
    internal_value["white_rating"] = white_data["rating"]
    internal_value["white_rating_diff"] = white_data["ratingDiff"]
      
    black_data = data.pop("players")["black"]
    internal_value["black"] = black_data["user"]
    internal_value["black_rating"] = black_data["rating"]
    internal_value["black_rating_diff"] = black_data["ratingDiff"]
      
    internal_value["created_at"] = data.pop("createdAt")
    internal_value["last_move_at"] = data.pop("lastMoveAt")
      
    internal_value["initial_time"] = data.get("clock").get("initial")
    internal_value["increment"] = data.get("clock").get("increment")
    internal_value["total_time"] = data.pop("clock").get("totalTime")
      
    return {**internal_value, **data}

    def create(self, validated_data):
        white = validated_data.pop("white")
        white_user, is_created = User.objects.get_or_create(id=white["id"], 
name=white["name"])
        black = validated_data.pop("black")
        black_user, is_created = User.objects.get_or_create(id=black["id"], 
name=black["name"])

        opening = validated_data.pop("opening")
        opening_model, is_created = 
Opening.objects.get_or_create(name=opening["name"], eco=opening["eco"])
        return Game.objects.get_or_create(white=white_user, 
black=black_user, opening=opening_model, **validated_data)[0]


I have two problems :

1. I don’t know if I am following good practices here. In particular, is it 
the right use of the "to_internal_value" function ?
2. Sometimes the json I receive contains additional fields, like 
"moderator":

"players":{
    "white":{
        "user":{
            "name":"player1",
            "id":"player1",
            **"moderator": True**
        }
    }
}

and my deserialization process fails because it contains an unexpected 
field.
How can I handle it and make it more resilient ?

-- 
You received this message because you are subscribed to the Google Groups 
"Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-rest-framework+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-rest-framework/f33c38a2-f36d-40d1-8013-2e0fdbceedbdn%40googlegroups.com.

Reply via email to