Django model serialization returns natural primary key value sample code

  • 2021-06-28 12:54:50
  • OfStack

scene

When designing a table structure, you will inevitably need to establish some foreign key associations.For example, there are two models:


from django.db import models

class Person(models.Model):
 username = models.CharField(max_length=100)
 birthdate = models.DateField()

class Book(models.Model):
 name = models.CharField(max_length=100)
 author = models.ForeignKey(Person, on_delete=models.CASCADE)

The field author of table Book is a foreign key to table Person. We try Django's native Serializer module to serialize the Book instance:


from django.core import serializers
book_json = serializers.serialize("json", Book.objects.get(pk=1))

The results of JSON serialization are as follows:


{
 "pk": 1,
 "model": "store.book",
 "fields": {
  "name": "Mostly Harmless",
  "author": 42
 }
}

This "author": 42 is equivalent to unknown to the user. What we need is the name of the user whose primary key is 42 in the Person table, which is the value of username.

Solution

The scheme of using models.Manager is mentioned in the section "Serialization" of the official Django document.During the search for solutions, you also came into contact with Django-REST-Framework (DRF), knowing that the Serializer module in DRF can also solve such problems.Let's compare the two solutions.

Option 1: models.Manager

According to the documentation, to return to the natural primary key, we need to define a model manager and create an get_by_natural_The key method follows:
from django.db import models


from django.db import models

class PersonManager(models.Manager):
 def get_by_natural_key(self, username):
  return self.get(username=username)

class Person(models.Model):
 username = models.CharField(max_length=100)
 birthdate = models.DateField()
 objects = PersonManager()

Then serialize the Book instance again:


from django.core import serializers
book_json = serializers.serialize("json", Book.objects.get(pk=1), use_natural_foreign_keys=True)

The new results are as follows:

{
"pk": 1,
"model": "store.book",
"fields": {
"name": "Mostly Harmless",
"author": ["DouglasAdams"]
}
}

If you need to modify the data model of other applications, such as using django.auth.User (the default authentication backend) as the foreign key to Book, you can use the proxy mode to accomplish this without modifying the User model and using the new model manager:


from django.db import models

class NewManager(models.Manager):
 # ...
 pass

class MyPerson(Person):
 objects = NewManager()

 class Meta:
  proxy = True

Overall, this solution perfectly solves the problems I'm experiencing, with a slightly larger amount of code, but also more flexibility.

Scenario 2: Serializer for DRF

Let's try the serialization module of Django-REST-Framework:


from rest_framework import serializers
from .models import Book

class BookSerializer(serializers.ModelSerializer):
 author_name = serializers.CharField(source='author.username')

 class Meta:
  model = Book
  fields = '__all__'

This code indicates that when serializing an Book instance, a new attribute author_is addedname, which is derived from the natural primary key username of the foreign key author instance defined by the source parameter.

Then the serialization process is performed:


queryset = Book.objects.get(pk=1)
BookSerializer(instance=queryset)

Serialization results:

{
"id": 1,
"name": "Mostly Harmless",
"author": 42,
"author_name": "DouglasAdams"
}

Of course, serializing a batch of Book instances is also possible:


queryset = Book.objects.all()
BookSerializer(instance=queryset, many=True)

Serialization results:

[
{
"id": 1,
"name": "Mostly Harmless",
"author": 42,
"author_name": "DouglasAdams"
},
{
"id": 2,
"name": "Harry Potter",
"author": 2,
"author_name": "JKRowling"
}
]

As you can see, using DRF's serialization module to return the natural primary key, not only does the code change less clearly, but it also works well. The serialized data is one level less and 10 points more friendly to the front end.

Option 3: Manually modify the serialized foreign key

Of course, the silliest and easiest way to think of it is to manually modify the corresponding foreign key value in the JSON string to be a natural primary key value after serialization.

This works like scenario 11, but we need to iterate through the substitutions when we encounter a query that results in a list.Also imagine 1, if we do this in each view, the code will get 10 bad points.This scheme is not recommended.

summary

By comparing the two serialization schemes, I personally prefer the elegant handling of DRF.Of course, in addition to serialization, DRF has many other features, such as paging, which is strongly recommended for learning.

Of course, there may not be the best technology solution available, so if you encounter such problems, choose the one that suits you best.There may be more ways to solve the problem of title, also welcome to leave a message to discuss!

Reference resources

docs.djangoproject.com/zh-hans/2.2... docs.djangoproject.com/en/2.2/topi... www.django-rest-framework.org/api-guide/f...

Related articles: