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所返回的