正在进入ing...

drf基于APIView实现一对多(ForeignKey)数据新增、更新

发布时间:2021-04-08 浏览量: 698 文章分类: python

之前一直有在看源码,但是其实没有实战过,所以这个地方我就一直没有更新。刚好最近有个项目在做管理后台的时候用到了。我就抽空整理处理做个备忘。这个在日常使用中还是非常频繁的。

我们就以 书籍、作者 来举例 。

数据结构层面

# models.py
class Author(models.Model):
    name = models.CharField(max_length=128)
    def __str__(self):
        return self.name
    class Meta:
        verbose_name = '作者'

class Book(models.Model):
    name = models.CharField(max_length=128)
    author = models.ForeignKey(Author,related_name='books',on_delete=models.CASCADE)
    def __str__(self):
        return self.name
    class Meta:
        verbose_name = '书籍'

序列化器

class AuthorSerializers(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = '__all__'


class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'
        depth = 1

    def create(self,validated_data,author_id):
        author_obj = Author.objects.get(id=author_id)
        return Book.objects.create(author=author_obj,**validated_data)
    def update(self,instance,validated_data):
        # 数据本身
        # 序列化后的数据
        author_id = validated_data.get('author')
        author_obj = Author.objects.filter(id=author_id).first()
        book_name = validated_data.get('name')
        instance.author = author_obj
        instance.name = book_name
        return instance.save()

关于上面BookSerializers.create方法BookSerializers.update方法先不要着急。

视图

为了直观便于理解,我们都使用最为简单的 APIViewResponse方便我们具体搞清楚每个请求类型的处理逻辑。实际生产环境建议使用ModelViewSet.

class AuthorView(APIView):
    def __init__(self):
        self.req = {
            'status_code':20000,
            'msg':None,
            'data':None
        }
    def get(self, request, *args, **kwargs):
        # 请求查询参数
        query_set = models.Author.objects.all()
        ser = serializers.AuthorSerializers(instance=query_set, many=True)
        self.req['data'] = ser.data
        return Response(self.req)

    def post(self,request,*args,**kwargs):
        # 新增请求
        ser = serializers.AuthorSerializers(data=request.data)
        if ser.is_valid():
            ser.save()
            self.req['msg'] = 新增成功
        else:
            self.req['status_code'] = 50000
            self.req['msg'] = str(ser.errors)
        return Response(self.req)

    def put(self,request,*args,**kwargs):
        # 更新
        name_id = request.data.get('name_id') # 需要修改的编号
        name_obj = models.Author.objects.filter(id=name_id).first()
        ser = serializers.AuthorSerializers(instance=name_obj,data=request.data)
        if ser.is_valid():
            ser.save()
            self.req['msg'] = '修改成功'
        else:
            self.req['status_code'] = 50000
            self.req['msg'] = str(ser.errors)
        return Response(self.req)


class BookView(APIView):
    def __init__(self):
        self.req = {
            'status_code':20000,
            'msg':None,
            'data':None
        }
    def get(self, request, *args, **kwargs):
        query_set = models.Book.objects.all()
        ser = serializers.BookSerializers(instance=query_set, many=True)
        self.req['data'] = ser.data
        return Response(self.req)

    def post(self, request, *args, **kwargs):
        ser = serializers.BookSerializers(data=request.data)

        if ser.is_valid():
            author_id = request.data.get('author')
            ser.create(validated_data=ser.validated_data, author_id=author_id)
            self.req['msg'] = '新增成功'
        else:
            self.req['status_code'] = 50000
            self.req['msg'] = str(ser.errors)
        return Response(self.req)

    def put(self,request,*args,**kwargs):
        # 更新
        books_id = request.data.get('books_id') # 需要更新的id
        # 说明这是要更新书籍的名字
        books_obj = models.Book.objects.filter(id=books_id).first()
        ser = serializers.BookSerializers(instance=books_obj,data=request.data)
        if ser.is_valid():
            ser.update(books_obj,request.data)
        else:
            self.req['status_code'] = 50000
            self.req['msg'] = str(ser.errors)
        return Response(self.req)

个人理解

通过将每个请求拆分开,虽然代码变的一大堆,但是这样方便了理解和阅读,首先是第一个类AuthorView因为他不涉及 一对多,和多对多的问题。 所以在实现新增(post)、修改(put)方法的时候,序列化器基本不需要什么额外的设置,非常简洁就能实现一个类的增、改、查(删自己实现就好了)。

这里只要注意一点 serializer 的参数问题 + 只传instance为序列化数据,这个时候要注意many告诉是单个还是多个即可; + 如果是新增数据就需要传递data参数 + 如果是修改已有数据instance为现有的数据,data是要更新的数据

不涉及外键的,直接调用save方法是可以直接保存的,不需要自己额外编写,但是需要注意,必须要先过滤一下is_valid()确认数据有效性。否则会报错You must call .is_valid() before calling .save()

接下来是BookView视图,这个就相对复杂一些。他主要涉及了外键问题。所以drf自带的功能没办法满足,我们需要自己定义createupdate方法。 由于drf本身并不知道外链的表的问题,所以需要我们通过自己实现。

BookView 的 get请求

这个想展示出外键数据的方法实在太多了,有兴趣的可以去看Django rest_framework 源码学习笔记(一)之序列化器 ,我就直接使用最简单的depth实现了。

BookView 的 post请求

这里就已经设计关联外键的存储问题了,以上面的例子来说就是如何在新增一本书的情况下,让他关联到我指定的作者,所以先实现了ser.create(),并且传了2个参数,第一个是我序列化后的和书籍类相关的参数(ser.validated_data),第二个参数是关联的作者数据(author_id) 在create()方法中,就基本一样了,直接查询对应的数据,然后让他创建返回即可。

BookView 的 put请求

其实这个是最恶心的,特别是一个比较大的表,如果关联很多个外键,那真的是比较恶心的。 首先我先找到了需要修改的书籍类,然后传给update参数,然后在update里面在吧需要更新的外键数据都取出来,分别在去获取到对应的对象模型类,然后在对应的去更新。

以上就是我自己找到的资料和整理出的一些学习结论,虽然更新、创建的方法很多,但是整体来说基本都是这样的一个模式,无非是放在序列化器中还是放在视图中,我个人是比较倾向于放在序列化器中,视图只要放逻辑即可。 在查询了drf的官网后,看到有一句这么说的“If you're supporting writable nested representations you'll need to write .create() or .update() methods that handle saving multiple objects.” 。所以针对这种问题,建议如果不是特别复杂的表,还是用更高级的drf类尽量来减少一些代码吧。