Version:

  • Django = = 2.0.1
  • Djangorestframework = = 3.7.7
  • Making the address

Other:

  • Writing RESTful apis with Django REST Framework (1. Build the base API)
  • Writing RESTful apis with Django REST Framework (3. Add comment module)

Add user and tag information to GET/API /posts/ API /posts/<pk>

Currently, there are only links in author and tag. I want a nested sequence with a link and a name, so I need to change the default ‘author’ and ‘tag’ in the Fields of PostSerializer to create a new tag and user seriaizer:

class UserSerializerLite(serializers.HyperlinkedModelSerializer): """ User serializer used in Post serializer that contains only 'URL' 'username' """ class Meta: model = User fields = ('url', 'username') class TagSerializerLite(serializers.HyperlinkedModelSerializer): Class Meta: model = Tag fields = (' URL ', 'name')Copy the code

Before class Meta in PostSerializer add:

author = UserSerializerLite(read_only=True)
tag = TagSerializerLite(many=True)
Copy the code

Tag and author are now nested sequences with urls and name/username

The reason is that every time you create a new post, you create a new Tag and reference it instead of referring to an existing one. Updating the post also updates the Tag

To solve this problem, simply override create() and update() in PostSeriaizer and add to PostSeriaizer:

@staticmethod def addtag(tags, post): """ Adds a tag to an incoming post. If the tag already exists, the relationship is an existing tag in the library. If the tag does not exist, the relationship is added to the tag. Tag :param post: Instance of the POST class """ for tag in tags: try: t = Tag.objects.get(name=tag['name']) post.tag.add(t) flag = True except ObjectDoesNotExist: flag = False if not flag: t = Tag.objects.create(name=tag['name']) post.tag.add(t) def create(self, validated_data): """ Rewrite create, take the tag from validATED_data, "" Tags = validated_data.pop('tags') Post = post.objects.create (**validated_data) if tags is not None: self.addtag(tags, post) return post def update(self, instance, validated_data): Update ('title', 'body', 'name', 'name', 'name', 'name', 'name', 'name', 'name' validated_data.get('title', instance.title) instance.body = validated_data.get('body', instance.body) instance.save() instance.tag.clear() tags = validated_data.get('tag') if tags is not None: self.addtag(tags, post) return instanceCopy the code

Add information about GET Tags Users

Currently, posts in tags Users only have links. I want ‘URL’, ‘title’, ‘author’ and ‘body’, so I create a new seriaizer:

class PostSerializerLite(serializers.HyperlinkedModelSerializer): """ for Tag, User serializer, "" author = UserSerializerLite(read_only=True) def to_representation(self, instance): Ret = super(PostSerializerLite, PostSerializerLite, self).to_representation(instance) excerpt = ret['body'] if str(excerpt).__len__() > 50: excerpt = excerpt[:50] + '... ' ret['body'] = excerpt return ret class Meta: model = Post fields = ('url', 'title', 'author', 'body')Copy the code

Overriding to_representation() truncates the information in ‘body’ because ‘body’ contains complete information that is not needed to fetch a list

Add the following in TagSerializer and UserSerializer:

posts = PostSerializerLite(many=True, read_only=True)
Copy the code
Another way to truncate the ‘body’ information

Add a method to Post in models.py:

def excerpt(self): excerpt = str(self.body) if excerpt.__len__() > 50: excerpt = excerpt[:50]+'... ' return excerptCopy the code

Then change the fields of PostSeriaizerLite to:

fields = ('url', 'title', 'author', 'excerpt')
Copy the code

Reduce GET/API /posts/ ‘body’ information

We only want to change the ‘body’ information at GET/API /posts/, so we can’t directly change serializer_class when we send a GET request to/API /posts/, The router will pass the request to PostViewSet, which will call the list() method in ListModelMixin, so we can just rewrite the list() method:

def list(self, request, *args, **kwargs):
    queryset = self.filter_queryset(self.get_queryset())

    page = self.paginate_queryset(queryset)
    if page is not None:
        serializer = self.get_serializer(page, many=True)
        return self.get_paginated_response(serializer.data)

    serializer = self.get_serializer(queryset, many=True)
    return Response(serializer.data)
Copy the code

The serializer instance is provided by self.get_serializer(page, many=True) or self.get_serializer(queryset, My idea is to replace get_seriaizer with PostSeriaizerLite

def list(self, request, *args, **kwargs):
    """
    重写 list
    GET /api/posts/ 时调用的序列化器默认是 PostSerializer, 改成 PostSerializerLite 去除多余信息
    """
    queryset = self.filter_queryset(self.get_queryset())

    page = self.paginate_queryset(queryset)
    if page is not None:
        serializer = PostSerializerLite(page, many=True, context={'request': self.request})
        return self.get_paginated_response(serializer.data)

    serializer = PostSerializerLite(queryset, many=True, context={'request': self.request})
    return Response(serializer.data)
Copy the code

Because HyperlinkedModelSerializer need to request us to add the context = {‘ request: the self request}

At the end

There are many other useful components in the REST Framework that make building apis very easy. Others:

  • Writing RESTful apis with Django REST Framework (1. Build the base API)
  • Writing RESTful apis with Django REST Framework (3. Add comment module)