如何更优雅地写Django REST framework

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

好比写视图函数,能够用继承APIView的方式或者继承Viewsets的方式,甚至直接写视图函数python

可是想要更加干净简洁的代码,仍是须要找到实现的最佳方式数据库

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

models.py

1.PositiveSmallIntegerField后端

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

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

三、tag = models.ForeignKey(Tag, related_name="project_tag")框架

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

四、user = models.ForeignKey(User, unique=True)函数

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

五、ImageField和FileField其实是CharFieldsui

serializers.py

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

例如

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

二、通常来讲,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

一、perform_create中的serializer.save()语句能够带参数,好比

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

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

二、尽可能使用objects.filter而不是get

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

三、过滤器的使用

  • 应避免在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所返回的

相关文章
相关标签/搜索