DRF 序列化器ORM查询技巧

前言:许久没有用Django和DRF了,最近又用了用,发现Django版本更新后发生了不少变化,因此记录下一些ORM的查询方法和与Serializer的配合

环境:

python==3.6.5
django==3.1.4
djangorestframework==3.12.2

0X00 建立Model

import uuid

from django.db import models


# 模型DEMO,共有三个模型,分别为作者,文章和书。假定各个模型的关联关系为:
# 作者可以有多篇文章,但每篇文章只能属于一个作者(一对多)
# 作者可以有多本书,且每本书可以有多个作者(多对多)

class Author(models.Model):
    name = models.CharField(max_length=50, null=False, blank=False, help_text='作者')
    introduction = models.TextField(null=False, blank=False, help_text='作者简介')


class Article(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    author_id = models.ForeignKey(Author, on_delete=models.SET_NULL, null=True, related_name='author')
    title = models.CharField(max_length=50, null=False, blank=False, help_text='文章标题')
    create_time = models.DateTimeField(auto_now=True)


class Book(models.Model):
    name = models.CharField(blank=False, null=False, max_length=255)
    authors = models.ManyToManyField(Author, related_name='book_author')

0X01 创建各个Model的Serializer

from rest_framework import serializers
from .models import Article, Author, Book


class AuthorSerializer(serializers.ModelSerializer):

    class Meta:
        model = Author
        fields = '__all__'


class BookSerializer(serializers.ModelSerializer):

    class Meta:
        model = Book
        fields = '__all__'


class ArticleSerializer(serializers.ModelSerializer):

    class Meta:
        model = Article
        exclude = ['create_time']

0X02 查询需求

1.多对多反向查询属于Author的所有Book(URL:'authors/')

PS:所谓的多对多关系,在数据库层面实际上就是新创建了一张表存储Author和Book的映射关系,以此达到多对多的目的,例如:

author_id   book_id
    1          1
    2          1
    1          2
    2          2

(1)查询语句

这是Django 3.1版本官方文档给出的多对多关系的反向查询例子,但是奇怪的是并不能查询成功,例如:

可以看到author object的所有方法和属性,并没有book_set,但是有book_author:

可以看到book_author为ManyRelatedManager object,通过这个对象就可以反向查询了,调用all()方法即可

(2)Serializer层

定义了ModelSerializer的情况下,调用Get方法默认只会返回Model中的字段,不会包含其他的,那么如果需要添加反向查询的books字段就需要在序列化器中添加一个字段及该字段的来源,如下面的代码,字段来源为一个函数:

class AuthorSerializer(serializers.ModelSerializer):
    books = serializers.SerializerMethodField(method_name='get_books', read_only=True)

    class Meta:
        model = Author
        fields = '__all__'

    # 反向查询该作者拥有的所有书名
    @staticmethod
    def get_books(obj):
        return obj.book_author.all().values('name')

obj.book_author.all()返回的为QuerySet,调用values方法可以指定返回对象的哪些字段,例如上面指定了只返回name字段,返回结果示例:

2.多对多正向查询编写了Book的所有Author(URL:'books/')

(1)查询语句

obj = Book.objects.get(id=1)
obj.authors.all()  # 获取编写了该Book的所有Author对象
obj.authors.all().values('name', 'introduction')  # 获取所有Author对象的name和intro的值,其他值不返回 

(2)Seriliazer层

默认情况,调用Get方法获取Book的话,只会返回编写了该Book的所有Author的主键,那么如果我们想要返回Author的其他字段怎么办?

1)返回Author的所有字段,使用嵌套序列化:

class BookSerializer(serializers.ModelSerializer):
    # 嵌套序列化,将author_id全部转换为Author model的字段
    authors = AuthorSerializer(many=True, read_only=True)

    class Meta:
        model = Book
        fields = '__all__'

需要注意的是此处定义的序列化器中的Field名须为authors,与Book Model中定义的多对多关系的Field名保持一致。

如果要定义成其他名称,则需要指定source字段,例如:

class BookSerializer(serializers.ModelSerializer):
    # 嵌套序列化,将author_id全部转换为Author model的字段
    new_define_authors = AuthorSerializer(many=True, read_only=True, source='authors')

    class Meta:
        model = Book
        fields = '__all__'

返回DEMO:

2)Author Model中含有敏感信息,只返回Author的部分字段该怎么办?

在<查询语句>中提到过,可以指定返回的字段,其他的字段不返回,那么可以在序列化器中使用SeriliazerMethodField,添加我们想返回的内容,例如:

class BookSerializer(serializers.ModelSerializer):
    # 同样是嵌套序列化,将author_id全部转换为Author model的字段,但是可以指定返回哪些字段,而不是Author的所有字段
    authors = serializers.SerializerMethodField(method_name='get_author_info', read_only=True)

    class Meta:
        model = Book
        fields = '__all__'

    # 获取值的函数,用于指定authors字段包含了Author Model的哪些字段
    @staticmethod
    def get_author_info(obj):
        return obj.authors.all().values('name', 'introduction')

注意事项同1)

返回DEMO:

3.一对多关系根据主键查询其他字段(URL:'articles/')

(1)查询语句

上述Model中定义的Author和Article就是一对多关系,一个作者可以对应多篇文章。Article表中只会保存作者的id,但是通过ORM获取的话,Django会自动返回一个Author Object,直接通过该Object就可以访问Author的各个属性了,例如:

如果想要配合Seriliazer使用,那么我们可以在Model层添加一些字段,例如添加Author.name和Author.introduction:

class Article(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    author_id = models.ForeignKey(Author, on_delete=models.SET_NULL, null=True, related_name='author')
    title = models.CharField(max_length=50, null=False, blank=False, help_text='文章标题')
    create_time = models.DateTimeField(auto_now=True)

    @property
    def author_name(self):
        return self.author_id.name

    @property
    def author_intro(self):
        return self.author_id.introduction

@property装饰器的作用是将函数转换为属性,可以直接通过Article Object访问author_name和author_intro

(2)Seriliazer层

在完成上述定义后,怎么在序列化器层与之配合呢?在序列化器中添加一个字段即可,例如只读字段:

class ArticleSerializer(serializers.ModelSerializer):
    author_name = serializers.ReadOnlyField()  # 返回作者名称字段,根据主键获取,见models.py
    author_intro = serializers.ReadOnlyField()  # 返回作者介绍字段,根据主键获取,见models.py

    class Meta:
        model = Article
        exclude = ['create_time']

返回DEMO:

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值