• 如何更优雅地写Django REST framework


    DRF(Django REST framework)是一个高度封装的框架,这导致想完成一件事情可以通过重写父类函数的方式从DRF的各个层次来写,都能够实现目的。

    比如写视图函数,可以用继承APIView的方式或者继承Viewsets的方式,甚至直接写视图函数

    但是想要更加干净简洁的代码,还是需要找到实现的最佳方式

    以下是我的一些个人总结,欢迎讨论

    models.py

    1.PositiveSmallIntegerField

    • Positive对应unsigned
    • Small对应smallint(5)
    • 对于一些数据量较小的系统可以使用这个Field作为id

    2、字段定义中verbose_name定义的是django自带接口ui的字段说明,help_text定义的是swagger的字段说明

    3、tag = models.ForeignKey(Tag, related_name="project_tag")

    • 定义一个外键会在数据库中生成一个名为tag_id的字段
    • 但是在模型实例中,tag是Tag模型的实例
    • 也就是说,Django的ORM会把tag.id=tag_id的Tag模型实例取出来放到tag字段

    4、user = models.ForeignKey(User, unique=True)

    • 相当于user = models.OneToOneField(User)
    • 外键 on_delete = models.CASCADE 级联删除是默认的选项

    5、ImageField和FileField实际上是CharFields

    serializers.py

    1、serializers中对字段做出的限制只会影响前端传到后端的数据,而不会影响后端传到前端的数据

    例如

    class MySerializer(serializers.ModelSerializer):
        TYPE = (
            # (0, "级别一"), #在model中这行没有注释掉
            (1, "级别二"),
            (2, "级别三")
        )
        # 这样可以限制前端不能传my_type=0的数据,但是my_type=0的数据可以在前端接收到
        my_type = ChoiceField(choices=TYPE,required=True)
    

    2、一般来说,update和create的操作都会在serializers中实现

    • 很多刚开始接触DRF的同学会习惯在view中写update和create,其实,在serializers中实现是一种更好的方法,
      因为,这样你的代码不用绕来绕去。不用费劲获取serializer的值再费劲存到serializer里,直接在serializer中实现就行了。
      别看create和update函数的源码那么长,其实不用管它们,整个重写就好了

    • update与create函数框架

    def create(self, validated_data):
        ...
        return instance
        
    def update(self, instance, validated_data):
        ...
        return instance
    

    validated_data是经过验证的前端数据,instance是用id获取的对应数据库数据的模型实例
    它们都要返回一个模型实例,作为返回前端的数据

    • update和create方法由serializer.save()函数调用
    • 在serializer中self.context["request"]相当于view中的self.request
    • 在Model.objects的创建或筛选中,可以直接拿一个模型实例赋值给外键字段或相比较,比如
    Model1.objects.create(user=self.context["request"].user,
                           foreign_key=foreign_key_instance)
    
    • 如果要用一个已有的模型实例的数据创建一条新数据,我曾用过一个不优雅的写法
    for key in update_data:
        setattr(instance, key, update_data[key])
    # 把对象转为字典,作为新建数据的参数
    dic = instance.__dict__
    del dic['id']
    del dic['_state']
    new_instance = Model1.objects.create(**dic)
    

    先把更新后的实例对象转为字典,再删掉id等在数据表插入新数据时不该传的数据,再将字典作为objects.create的参数

    其实有一个更巧妙的方法

    instance.id = None
    for attr, value in update_data.items():
        setattr(instance, attr, value)
    instance.save()
    

    instance.save()之后,instance将会变成新插入数据的模型实例

    • 某些情况需要父类函数的写法,不需要复制代码,用super就可以了
    super(Model1Serializer, self).update(instance, validated_data)
    

    views.py

    1、perform_create中的serializer.save()语句可以带参数,比如

    user_id = self.request.user.id
    serializer.save(user=User.objects.get(id=user_id))
    

    实现从request中获取user的值,而不是从表单

    2、尽量使用objects.filter而不是get

    • filter返回一个数组,get返回一个数据库实例
    • 如果get()中的过滤条件没有匹配出数据,get().delete()会报错,filter则会取出一个空数组,不会报错

    3、过滤器的使用

    • 应避免在get_queryset()中使用复杂的逻辑,比如
    def get_queryset(self):
        key_1 = self.request.key1
        key_2 = self.request.key2
        my_type = self.request.query_params.get('type', None)
        if my_type == 1:
            return Model.objects.filter(foreign_key_1=key_1)
        elif my_type == 2:
            return Model.objects.filter(foreign_key_2=key_2)
        # 默认情况,返回所有
        return Model.objects.all()
    

    其实这就是一个根据查询参数过滤的过程,完全可以使用过滤器实现,这样在Django自带ui中也会有过滤器的说明

    • 要使用过滤器,首先安装库
      pip install django-filter

    python2要特别指定django-filter==1.1

    • 然后在settings的INSTALLED_APPS中加上django_filters
    • 新建一个名为filters.py的文件,定义一个过滤器
    class MyFilter(django_filters.rest_framework.FilterSet):
        MY_TYPE = (
            (1, "类别一"),
            (2, "类别二")
        )
        
        type = django_filters.ChoiceFilter(help_text="类型",
                                           label="类型",
                                           choices=MY_TYPE,
                                           method="type_filter"
                                           )
    
        def type_filter(self,queryset,name,value):
            key_1 = self.request.key1
            key_2 = self.request.key2
            if value == 1:
                return queryset.filter(foreign_key_1=key_1)
            elif value == 2:
                return queryset.filter(foreign_key_2=key_2)
    
        class Meta:
            model = Tag
            fields = ['type']
    
    • 在viewset中加上
    filter_backends = (DjangoFilterBackend, )
    filter_class = MyFilter
    

    而get_queryset函数只需要一句return Model.objects.all()就好

    注意type_filter的queryset就是get_queryset所返回的

  • 相关阅读:
    最近工作状态异常的原因追寻。
    当“逻辑”与“UE”冲突时
    面对一个“丢失了与用户“签订”的协议的修改”时进行的思考。
    如果公司的需求总是让研发部门有怨言…
    安装sybase12.0,运行时报错异常。
    你看到这份文档,我就想摔鼠标!
    对于研发组长的责任产生了疑惑。
    关于html中空格导致的排版问题
    如何配置你的工作环境。
    今天的笔记:2014年6月3日
  • 原文地址:https://www.cnblogs.com/luozx207/p/10491920.html
Copyright © 2020-2023  润新知