djangodjango-serializerdjango-select-relateddjango-prefetch-related

using select_related with serializer in Django Rest Framework


class User(models.Model):
    name = Charfield(...)
    age = Integerfield(...)
    ...

class Article(models.Model):
    user = ForeignKey(User, ...)
    title = Charfield(...)
    content = Charfield(...)
    ...

class ArticleView(ViewSet):
    ...
    queryset = Article.objects.all().select_related('user')  
    queryset = Article.objects.all().prefetch_related('user')


class ArticleSerializer(ModelSerializer):
    class Meta:
        model = Article
        fields = "__all__"


1. If I just apply select_related (or prefetch_related), will the user's information be added to my query set?

2. If so, when serializing the data, the information of the user who innerjoined through select_related is not shown in the serializer.data returned after serialization.

2-1. If I had to write additional code on my serializer, Which of the following options should I use?

class ArticleSerializer(ModelSerializer):
    class Meta:
        model = Article
        fields = "__all__"
        depth = 1

        or

class ArticleSerializer(ModelSerializer):
    user = UserSerializer()

    class Meta:
        model = Article
        fields = "__all__"

2-2. The above options can get the same user's information without selecting_related(or prefetch_related), so why do you use select_related(or prefetch_related)?

Is there a difference in the number of DB lookup?


Solution

    1. If I just apply select_related (or prefetch_related), will the user's information be added to my query set?

    Yes, select_related will make a join query and fill user attribute with an User instance. Without select_related, when you access user attribute, a extra query will be made to get User instance.

    select_related()

    Returns a QuerySet that will “follow” foreign-key relationships, selecting additional related-object data when it executes its query. This is a performance booster which results in a single more complex query but means later use of foreign-key relationships won’t require database queries.

    prefetch_related() is used for many-to-many and many-to-one relationship.

    prefetch_related, on the other hand, does a separate lookup for each relationship, and does the ‘joining’ in Python. This allows it to prefetch many-to-many and many-to-one objects, which cannot be done using select_related, in addition to the foreign key and one-to-one relationships that are supported by select_related.

    1. If so, when serializing the data, the information of the user who innerjoined through select_related is not shown in the serializer.data returned after serialization.

    Because default serializer field for ForeignKey is PrimaryKeyRelatedField which represent the target of the relationship using its primary key.

    Refer to ModelSerializer document.

    Any relationships such as foreign keys on the model will be mapped to PrimaryKeyRelatedField. Reverse relationships are not included by default unless explicitly included as specified in the serializer relations documentation.

    2-1. If I had to write additional code on my serializer, Which of the following options should I use?

    depth=1 is equal to define a nested model serializer with fields='__all__' for all ForeignKey and OneToOneField. So in your case, 2 serializers are same.

    Refer to source code

        def build_nested_field(self, field_name, relation_info, nested_depth):
            """
            Create nested fields for forward and reverse relationships.
            """
            class NestedSerializer(ModelSerializer):
                class Meta:
                    model = relation_info.related_model
                    depth = nested_depth - 1
                    fields = '__all__'
    
            field_class = NestedSerializer
            field_kwargs = get_nested_relation_kwargs(relation_info)
    
            return field_class, field_kwargs
    

    2-2. The above options can get the same user's information without selecting_related(or prefetch_related), so why do you use select_related(or prefetch_related)?

    You shuld use prefetch_related, because it use one query to fetch Articles and Users. Say you have 100 articles, if you don't use prefetch_related, 100 extra query will be made when you access article.user.

    Summary, serializer serialize data according to the fields defined by you, not according to the fields fetched in the queryset. ModelSerializer have default serializer fields for all the model fields, if the default serializer fields are not what you want, you should define serializer fields explicitly.