前言:许久没有用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: