django-rest-framework 基礎二 序列化器和路由
django-rest-framework 基礎二 序列化器和路由
1. 序列化器
1. 序列化,序列化器會把模型對象(qs,book)轉換成字典,經過response以後變成json字符串
2. 反序列化,把客戶端發(前端)送過來的數據,經過request以後變成字典(data),序列化器可以把字典轉成模型-->存到數據庫中
3. 反序列化,完成數據校驗功能---》前端傳入的數據是否合法,長度夠不夠等等, 進行數據校驗
1.1 Serializer的使用
使用序列化器完成增刪改查接口
準備數據,modles.py
from django.db import models
# Create your models here.
class Book(models.Model):
name = models.CharField(max_length=128)
auth = models.CharField(max_length=128)
price = models.DecimalField(decimal_places=2, max_digits=5)
"""
增加一個書籍表
數據庫遷移:
python3 manage.py makemigrations
python3 manage.py migrate
如果之前這些步驟做過,可以忽略
"""
序例化文件serializers.py
from rest_framework import serializers
from drftest.models import Book
class BookSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
name = serializers.CharField(min_length=3) # 不但序列化,而且限制最小長度不能小於3
auth = serializers.CharField()
price = serializers.DecimalField(decimal_places=2, max_digits=5)
def create(self, validated_data):
# validated_data校驗過後的數據
book = Book.objects.create(**validated_data)
return book # 返回新增的對象
def update(self, instance, validated_data):
# instance為要修改的對象
# validated_data校驗過後的數據
instance.name = validated_data.get('name')
instance.auth = validated_data.get('auth')
instance.price = validated_data.get('price')
instance.save() # 模型對象自帶的save,保存到數據庫中(必須要save,否則只修改了數據,但沒有保存到數據庫里)
return instance
"""
必須要重寫create和update 因為save里只是定義了,但沒具體實現,因為不知道具體存到哪個表中,所以要在序列化類中實現
def update(self, instance, validated_data):
raise NotImplementedError('`update()` must be implemented.')
def create(self, validated_data):
raise NotImplementedError('`create()` must be implemented.')
"""
視圖函數views.py
from drftest.serializers import BookSerializer
from drftest.models import Book
from rest_framework.views import APIView
from rest_framework.response import Response
class BookView(APIView):
# 查全部的數據
def get(self,request):
book_list = Book.objects.all()
res = BookSerializer(instance=book_list, many=True)
return Response(res.data)
# 新增一條的數據
def post(self, request):
res = BookSerializer(data=request.data)
if res.is_valid(): # 校驗數據
res.save() # 校驗通過保存數據,保存時要重寫create 方法,在序列化文件里
return Response(res.data)
return Response({"code" : 1001, "msg" : "數據驗證失敗", "error" : res.errors})
class BookViewDetail(APIView):
# 查某一條的數據
def get(self,request,pk):
book_list = Book.objects.filter(pk=pk).first()
res = BookSerializer(instance=book_list)
return Response(res.data)
# 修改某一條的數據
def put(self,request, pk):
book_list = Book.objects.filter(pk=pk).first()
# 既有instance,又有data,表示修改
res = BookSerializer(instance=book_list, data=request.data)
if res.is_valid(): # 校驗數據
res.save() # 校驗通過保存時要重寫update 方法,在序列化文件里
return Response(res.data)
return Response({"code": 1001, "msg": "數據驗證失敗", "error": res.errors})
# 刪除某一條的數據
def delete(self, request, pk):
res = Book.objects.filter(pk=pk).delete()
print(res)
return Response({"code": 1002, "msg": "數據刪除成功"})
路由urls.py
from django.contrib import admin
from django.urls import path
from drftest import views
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BookView.as_view()),
path('books/<int:pk>/', views.BookViewDetail.as_view()),
]
示例:
查全部數據
查某一條數據:
新增一條數據:
修改一條數據(修改價格為12.9)
刪除一條數據:
總結
-
第一步:寫一個類:必須繼承
drf
中的Serializer
及其子類 -
第二步:在類中寫要序列化的字段–>要序列化哪些,就寫哪些,不序列化的不寫
-
第三步:使用序列化類,視圖類中用
得到序列化類對象
對象.data
,通過Response
返回給前端
1.2 序列化器中的字段類型
字段 | 字段構造方式 |
---|---|
BooleanField | BooleanField() |
NullBooleanField | NullBooleanField() |
CharField | CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True) |
EmailField | EmailField(max_length=None, min_length=None, allow_blank=False) |
RegexField | RegexField(regex, max_length=None, min_length=None, allow_blank=False) |
SlugField | SlugField(maxlength=50, min_length=None, allow_blank=False) 正則字段,驗證正則模式 [a-zA-Z0-9-]+ |
URLField | URLField(max_length=200, min_length=None, allow_blank=False) |
UUIDField | UUIDField(format=』hex_verbose』) format: 1) 'hex_verbose' 如"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex' 如 "5ce0e9a55ffa654bcee01238041fb31a" 3)'int' – 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a" |
IPAddressField | IPAddressField(protocol=』both』, unpack_ipv4=False, **options) |
IntegerField | IntegerField(max_value=None, min_value=None) |
FloatField | FloatField(max_value=None, min_value=None) |
DecimalField | DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位數 decimal_palces: 小數點位置 |
DateTimeField | DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None) |
DateField | DateField(format=api_settings.DATE_FORMAT, input_formats=None) |
TimeField | TimeField(format=api_settings.TIME_FORMAT, input_formats=None) |
DurationField | DurationField() |
ChoiceField | ChoiceField(choices) choices與Django的用法相同 |
MultipleChoiceField | MultipleChoiceField(choices) |
FileField | FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ImageField | ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ListField | ListField(child=, min_length=None, max_length=None) |
DictField | DictField(child=) |
其中ListField
和DictField
是models
中沒有的類型,在反序列化時,如果前端傳入列表或字典可以使用這兩字段進行反序列化。
1.2.1 字段參數
寫在字段類中的參數
選項參數:
參數名稱 | 作用 |
---|---|
max_length | 最大長度(CharField) |
min_lenght | 最小長度(CharField) |
allow_blank | 是否允許為空(CharField) |
trim_whitespace | 是否截斷空白字符(CharField) |
max_value | 最小值 (IntegerField) |
min_value | 最大值(IntegerField) |
通用參數:
參數名稱 | 說明 |
---|---|
read_only | 表明該字段僅用於序列化輸出,默認False |
write_only | 表明該字段僅用於反序列化輸入,默認False |
required | 表明該字段在反序列化時必須輸入,默認True |
default | 反序列化時使用的默認值 |
allow_null | 表明該字段是否允許傳入None,默認False |
validators | 該字段使用的驗證器(不太用) |
error_messages | 包含錯誤編號與錯誤信息的字典 |
label | 用於HTML展示API頁面時,顯示的字段名稱 |
help_text | 用於HTML展示API頁面時,顯示的字段幫助提示信息 |
通用參數中重點的兩個:
"""
read_only:表明該字段僅用於序列化輸出,默認False
如果 read_only=True,這個字段只用來做序列化
把對象---》json給前端
write_only:表明該字段僅用於反序列化輸入,默認False
如果 write_only=True,這個字段只用來做反序列化
前端json---》存到數據庫
什麼都不寫,表示既序列化,又反序列化
read_only=True 序列化給前端,前端看到的字段,但前端傳數據的時候可以不傳這個對應的字段
write_only=True 反序列化時,前端需要傳什麼過的字段
但一個字段里不能即寫read_only=True又寫write_only=True。
示例:
id = serializers.CharField(read_only=True)
name=serializers.CharField(max_length=32,min_length=3,)
auth=serializers.CharField(write_only=True)
"""
1.3 序列化時,定製序列化的字段
例如定製一個price_info
字段
方法一:在序列化類中寫
class BookSerializer(serializers.Serializer):
...
price_info = serializers.SerializerMethodField()
# 使用SerializerMethodField方法,下面必須要寫一個 以get_開頭後跟自定義字段名的函數
def get_price_info(self,obj):
return "price is " + str(obj.price)
...
# 只在序列化中增加,其他的內容不變
"""
class ExampleSerializer(self):
extra_info = SerializerMethodField()
def get_extra_info(self, obj):
return ... # Calculate some data to return.
"""
方法二:在models中寫方法
"""models.py"""
from django.db import models
# Create your models here.
class Book(models.Model):
name = models.CharField(max_length=128)
auth = models.CharField(max_length=128)
price = models.DecimalField(decimal_places=2, max_digits=5)
#以下為新增的自定義的字段
@property
def price_info(self):
return "price is " + str(self.price)
# 在序列化類中
"""Serializers.py"""
class BookSerializer(serializers.Serializer):
...
#models里定義,序列化中使用 read_only=True 只有序列化中使用
price_info = serializers.CharField(read_only=True)
...
# 其他的內容不變
上面的兩個方法的效果一樣。
1.4 局部勾子和全局勾子
驗證順序:
先走字段選項參數的規則,再走局部鉤子,最後是走全局鉤子
1.4.1 字段選項參數的規則:
"""serializers.py"""
from rest_framework import serializers
from drftest.models import Book
class BookSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
name = serializers.CharField(min_length=3) # 不但序列化,而且限制最小長度不能小於3
auth = serializers.CharField()
price = serializers.DecimalField(decimal_places=2, max_digits=5)
"""
字段選項參數的規則:
name = serializers.CharField(min_length=3) 限制書籍名字最小長度不能小於3
price = serializers.DecimalField(decimal_places=2, max_digits=5)
"""
書籍名字為不符合規範
書籍名字為不符合規範並價錢也不符合
1.4.2 局部勾子驗證
class BookSerializer(serializers.Serializer):
def create(self, validated_data):pass
def update(self, instance, validated_data):
# 局部鉤子,只驗證某一個字段
# 要驗證哪個字段, 必須要validate_開後,後面跟字段名,如:validate_name
def validate_name(self,attr):
if attr.startswith('xx'):
raise ValidationError("名字不能以xx開頭")
else:
return attr # 沒有問題,正常返回
驗證(雖然符合字段的規範,但不符合局部勾子的規範)
1.4.3 全部勾子
class BookSerializer(serializers.Serializer):
def create(self, validated_data):pass
def update(self, instance, validated_data):
def validate(self, attrs):
# attrs校驗過後的數據
if attrs.get('name') == attrs.get('auth'):
raise ValidationError('作者名不能等於書名')
else:
return attrs
1.5 ModelSerializer模型類序列化器
上面使用的序列化器寫出的接口,在新增和修改的時候必須要重寫create
和update
方法,可以使用ModelSerializer
跟表模型做綁定,就不需要重寫create
和update
方法了。
為了和之前寫的做區分,繼承ModelSerializer
類的為第二個版本_v2
序列化serializers.py
class BookSerializer_v2(serializers.ModelSerializer):
class Meta:
model = Book
# fields = '__all__' # 拿全部字段
fields = ['id','name','auth', 'price','price_info']
# 沒有create 和updata方法了
# 局部勾子
def validate_name(self, attr):
if attr.startswith('YY'):
raise ValidationError("名字不能以YY開頭")
else:
return attr # 沒有問題,正常返回
# 全局勾子
def validate(self, attrs):
# attrs校驗過後的數據
if attrs.get('name') == attrs.get('auth'):
raise ValidationError('作者名不能等於書名')
else:
return attrs
視圖views.py
class BookView_v2(APIView):
def get(self,request):
book_list = Book.objects.all()
res = BookSerializer_v2(instance=book_list, many=True)
return Response(res.data)
def post(self, request):
res = BookSerializer_v2(data=request.data)
if res.is_valid():
res.save()
return Response(res.data)
return Response({"code" : 1001, "msg" : "數據驗證失敗", "error" : res.errors})
class BookViewDetail_v2(APIView):
def get(self, request, pk):
book_list = Book.objects.filter(pk=pk).first()
res = BookSerializer_v2(instance=book_list)
return Response(res.data)
def put(self, request, pk):
book_list = Book.objects.filter(pk=pk).first()
# 既有instance,又有data,表示修改
res = BookSerializer_v2(instance=book_list, data=request.data)
if res.is_valid():
res.save()
return Response(res.data)
return Response({"code": 1001, "msg": "數據驗證失敗", "error": res.errors})
def delete(self, request, pk):
res = Book.objects.filter(pk=pk).delete()
print(res)
return Response({"code": 1002, "msg": "數據刪除成功"})
路由urls.py
rom django.contrib import admin
from django.urls import path
from drftest import views
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BookView.as_view()),
path('books/<int:pk>/', views.BookViewDetail.as_view()),
path('books_v2/', views.BookView_v2.as_view()),
path('books_v2/<int:pk>/', views.BookViewDetail_v2.as_view()),
]
訪問新接口:
修改一條數據
原數據:
修改價格為:12.8
使用ModelSerializer
不用寫create
和update
方法,依然可以修改和新增。
1.5.1 增加額外的參數
上面的方法不用寫create
和update
方法了,但是如果想在使用字段選項參數,沒辦法直接傳參數了。
解決方法:
extra_kwargs = {'字段名':{參數:值}}
序列化serializers.py
class BookSerializer_v2(serializers.ModelSerializer):
class Meta:
model = Book
# fields = '__all__' # 拿全部字段
fields = ['id','name','auth', 'price','price_info']
# 使用字段選項參數
extra_kwargs = {
'id':{'read_only':True},
'price_info':{'read_only':True},
'name':{'min_length':3,'max_length':5}
}
def validate_name(self, attr):
if attr.startswith('YY'):
raise ValidationError("名字不能以YY開頭")
else:
return attr # 沒有問題,正常返回
# 全局勾子
def validate(self, attrs):
# attrs校驗過後的數據
if attrs.get('name') == attrs.get('auth'):
raise ValidationError('作者名不能等於書名')
else:
return attrs
# 如果也想在這裏面定製字段,比如像上面一樣,增加price_info,
# price_info,它不是數據庫中字段,但也要在fields中註冊
fields = ['id','name','auth', 'price','price_info']
# price_info字段的增加也只能使用在models.py中寫方法了
"""models.py代碼:"""
from django.db import models
# Create your models here.
class Book(models.Model):
name = models.CharField(max_length=128)
auth = models.CharField(max_length=128)
price = models.DecimalField(decimal_places=2, max_digits=5)
@property
def price_info(self):
return "price is " + str(self.price)
1.6 序列化多表操作
拿書籍的數據時,不但要拿基本信息,還要拿對應的出版社和作者
序列化serializers.py
from rest_framework import serializers
from drftest.models import Book
from django.core.exceptions import ValidationError
class BookSerializer_v2(serializers.ModelSerializer):
class Meta:
model = Book
# fields = '__all__' # 拿全部字段
fields = ['id','name','auth', 'price','price_info','publish_list','auth_list']
extra_kwargs = {
'id':{'read_only':True},
'price_info':{'read_only':True},
'price':{'write_only':True},
'name':{'min_length':3,'max_length':5},
'publish_list':{'read_only':True},
'auth_list':{'read_only':True},
}
def validate_name(self, attr):
if attr.startswith('YY'):
raise ValidationError("名字不能以YY開頭")
else:
return attr # 沒有問題,正常返回
# 全局勾子
def validate(self, attrs):
# attrs校驗過後的數據
if attrs.get('name') == attrs.get('auth'):
raise ValidationError('作者名不能等於書名')
else:
return attrs
models.py
from django.db import models
# Create your models here.
class Book(models.Model):
name = models.CharField(max_length=128)
auth = models.CharField(max_length=128)
price = models.DecimalField(decimal_places=2, max_digits=5)
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE,default=1)
bookToAuth = models.ManyToManyField(to='Authors')
@property
def price_info(self):
return "price is " + str(self.price)
@property
def publish_list(self):
#單條數據
return {'name':self.publish.name,"address":self.publish.address,'phone':self.publish.phone}
@property
def auth_list(self):
# 多條數據
l = []
for auth in self.bookToAuth.all():
l.append({"name":auth.name, 'city':auth.city})
return l
class Publish(models.Model):
name = models.CharField(max_length=255)
address = models.CharField(max_length=255)
phone = models.CharField(max_length=20)
class Authors(models.Model):
name = models.CharField(max_length=128)
city = models.CharField(max_length=128)
authdetail = models.ForeignKey(to='AuthDetail', on_delete=models.CASCADE,default=1)
class AuthDetail(models.Model):
address = models.CharField(max_length=255)
phone = models.CharField(max_length=20)
views.py
class BookView_v2(APIView):
def get(self,request):
book_list = Book.objects.all()
res = BookSerializer_v2(instance=book_list, many=True)
return Response(res.data)
def post(self, request):
res = BookSerializer_v2(data=request.data)
if res.is_valid():
res.save()
return Response(res.data)
return Response({"code" : 1001, "msg" : "數據驗證失敗", "error" : res.errors})
class BookViewDetail_v2(APIView):
def get(self, request, pk):
book_list = Book.objects.filter(pk=pk).first()
res = BookSerializer_v2(instance=book_list)
return Response(res.data)
def put(self, request, pk):
book_list = Book.objects.filter(pk=pk).first()
# 既有instance,又有data,表示修改
res = BookSerializer_v2(instance=book_list, data=request.data)
if res.is_valid():
res.save()
return Response(res.data)
return Response({"code": 1001, "msg": "數據驗證失敗", "error": res.errors})
def delete(self, request, pk):
res = Book.objects.filter(pk=pk).delete()
print(res)
return Response({"code": 1002, "msg": "數據刪除成功"})
urls.py
rom django.contrib import admin
from django.urls import path
from drftest import views
urlpatterns = [
path('admin/', admin.site.urls),
path('books_v2/', views.BookView_v2.as_view()),
path('books_v2/<int:pk>/', views.BookViewDetail_v2.as_view()),
]
注意: 查詢、修改和刪除都沒有問題,但是新增有問題,因為涉及到多表,而且Book
表中的publish
字段為主鍵和Publish
表相關聯。直接新增會報錯,要先在Publish
表中有了相關數據才能新增,同理Authors
表中新增也一樣,它和AuthDetail
相關聯。所以對應的表中都了數據才新增成功。
1.7 編寫視圖函數
之前繼承APIView
編寫視圖函數,現在可以使用GenericAPIView
它繼承了APIView
,比之前多了一些屬性和方法。
1.7.1 編寫視圖函數第二種方法
繼承GenericAPIView
方法寫視圖函數(以publish
表為例,編寫5個接口),其他的內容不動。
views.py
from django.shortcuts import render
from drfViews.serializers import BookSerializer, BookSerializer_v2, PublishSerializer,AuthDetailSerializer,AuthorsSerializer
from drfViews.models import Book, Publish,AuthDetail, Authors
from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
class PublishView(GenericAPIView):
queryset = Publish.objects.all() # 這個Publish表中全部數據,名字必須為queryset
serializer_class = PublishSerializer # 用來序列化的類, 名字必須為serializer_class
def get(self,request):
obj = self.get_queryset() # get_queryset就是queryset
# ser = self.serializers(instance=obj,many=True)
ser = self.get_serializer(instance=obj, many = True) # 和上面的代碼同樣功能
return Response(ser.data)
def post(self,request):
# ser = PublishSerializer(data=request.data)
ser = self.get_serializer(data=request.data) # 和上面的代碼同樣功能
if ser.is_valid():
ser.save()
return Response({"code": 2000, 'msg': '數據新增成功', 'data': ser.data})
return Response({"code": 4000, 'msg': '數據檢驗失敗', 'errors': ser.errors})
class PublishViewDetail(GenericAPIView):
queryset = Publish.objects.all()
serializer_class = PublishSerializer
def get(self,request, *args, **kwargs):
# publish = Publish.objects.all().filter(pk=pk).first()
obj = self.get_object() # 和上面的代碼同樣功能
# ser = PublishSerializer(instance=publish)
ser = self.get_serializer(instance=obj) # 和上面的代碼同樣功能
return Response(ser.data)
def put(self,request, *args, **kwargs):
obj = self.get_object()
ser = self.get_serializer(instance = obj, data = request.data) # # 既有instance,又有data,表示修改
if ser.is_valid():
ser.save()
return Response({"code": 2001, 'msg': '數據修改成功', 'data': ser.data})
return Response({"code": 4000, 'msg': '數據檢驗失敗', 'errors': ser.errors})
def delete(self,request, *args, **kwargs):
obj = self.get_object().delete()
return Response({"code": 2002, 'msg': '數據刪除成功'})
路由ulrs.py
from django.contrib import admin
from django.urls import path, include
from drfViews import views
urlpatterns = [
path('admin/', admin.site.urls),
path('publish/',views.PublishView.as_view()),
path('publish/<int:pk>/',views.PublishViewDetail.as_view()),
]
GET查全部
POST 新增數據
GET 查一條數據
PUT 修改數據
DELETE 刪除數據
1.7.2 編寫視圖函數第三種方法
使用rest_framework.mixins
裏面的五個擴展視圖類配合GenericAPIView
:
from rest_framework.mixins import
ListModelMixin, # 列出所有數據集(get查所有)
CreateModelMixin, # 創建實例(post創建數據)
DestroyModelMixin, # 刪除實例 (delete刪除數據)
RetrieveModelMixin, # 檢索實例 (get查一條數據)
UpdateModelMixin # 更新實例 (put更新數據)
這5個是視圖擴展類(不是視圖類,沒有集成APIView,需要配合GenericAPIView),這五個類是單獨的,它們沒用繼承其他類
這五個擴展視圖類在使用的時候,一定要配合GenericAPIView
(以Authors
表為例,編寫5個接口)
views.py
from django.shortcuts import render
from drfViews.serializers import BookSerializer, BookSerializer_v2, PublishSerializer,AuthDetailSerializer,AuthorsSerializer
from drfViews.models import Book, Publish,AuthDetail, Authors
from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import CreateModelMixin,ListModelMixin,DestroyModelMixin,RetrieveModelMixin,UpdateModelMixin
from rest_framework.response import Response
# 取全部數據和新增,繼承CreateModelMixin,ListModelMixin
class AuthorsView(GenericAPIView,CreateModelMixin,ListModelMixin):
queryset = Authors.objects.all() # 拿到實例
serializer_class = AuthorsSerializer # 序列化類
def get(self,request):
return super().list(request) # 取全部數據
def post(self,request):
return super().create(request) # 取新增數據
class AuthorsDetailView(GenericAPIView, UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin):
queryset = Authors.objects.all()
serializer_class = AuthorsSerializer
def get(self, request, *args, **kwargs):
return super().retrieve(request, *args, **kwargs) # 取某指定數據(一條)
def put(self, request, *args, **kwargs):
return super().update(request, *args, **kwargs) # 更新數據
def delete(self,request, *args, **kwargs):
return super().destroy(request, *args, **kwargs) # 刪除數據
路由urls.py
from django.contrib import admin
from django.urls import path
from drfViews import views
urlpatterns = [
path('admin/', admin.site.urls),
path('author/', views.AuthorsView.as_view()),
path('author/<int:pk>/', views.AuthorsDetailView.as_view()),
]
由於Authors
和AuthDetail
表有外鍵關聯,在新增作者的時候,作者詳情表要先有對應的數據才能新增成功,
所以在新增作者的時候要麼先新增作者詳情表,要麼在models.py
的Authors
裏面重寫create
方法新增這兩個表的數據。
在serializers.py
的AuthorSerialzier
裏面重寫create
方法:
serializers.py
address = serializers.CharField(write_only=True)
phone = serializers.CharField(max_length=20, write_only=True)
def create(self,validated_data):
datail = AuthDetail.objects.create(address=validated_data.get('address'), phone=validated_data.get('phone'))
print(datail)
author = Authors.objects.create(authdetail=datail, name=validated_data.get('name'),city=validated_data.get('city'))
return author
1.7.3 編寫視圖函數第四種方法
通過9個視圖子類,編寫視圖函數.
from rest_framework.generics import
CreateAPIView, # 創建 (POST)
ListAPIView, # 顯示 (GET查全部)
DestroyAPIView, # 刪除 (delete)
RetrieveAPIView, # 篩選 (GET查某一條)
UpdateAPIView, # 更新 (UPDATE)
ListCreateAPIView, # 創建和顯示 (POST、GET全部)
RetrieveUpdateAPIView, #篩選和更新 (GET查某一條、UPDATE)
RetrieveUpdateDestroyAPIView, # 篩選,更新和刪除 (GET查某一條、UPDATE和DELETE)
RetrieveDestroyAPIView # 篩選和刪除 (GET查某一條和DELETE)
繼承了這些類後,裏面的增刪改查接口都現實了。
以AuthDetail
表為例實現五個接口
views.py
from rest_framework.generics import CreateAPIView, ListAPIView,UpdateAPIView,RetrieveAPIView, DestroyAPIView
class AuthDetailView(ListAPIView,CreateAPIView):
# 查詢所有和新增
queryset = AuthDetail.objects.all()
serializer_class = AuthDetailSerializer
class AuthDetail_detailView(RetrieveAPIView,UpdateAPIView,DestroyAPIView):
# 查詢單條,刪除,修改
queryset = AuthDetail.objects.all()
serializer_class = AuthDetailSerializer
urls.py
from django.contrib import admin
from django.urls import path, include
from drfViews import views
urlpatterns = [
path('admin/', admin.site.urls),
path('authDetail/', views.AuthDetailView.as_view()),
path('authDetail/<int:pk>/', views.AuthDetail_detailView.as_view()),
]
GET
獲取所有
POST
新增
GET
獲取一條
PUT
修改
DELETE
刪除
除了這五個類之外還有四個組合的類
from rest_framework.generics import
ListCreateAPIView,
RetrieveUpdateAPIView,
RetrieveUpdateDestroyAPIView,
RetrieveDestroyAPIView,
使用這四個組合的類:
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView, RetrieveUpdateAPIView,RetrieveDestroyAPIView
class AuthDetailView(ListCreateAPIView):
# 就相當於ListAPIView,CreateAPIView 查詢所有和新增
queryset = AuthDetail.objects.all()
serializer_class = AuthDetailSerializer
class AuthDetail_detailView(RetrieveUpdateDestroyAPIView):
# 相當於:RetrieveAPIView,UpdateAPIView,DestroyAPIView 查詢單條,刪除,修改
queryset = AuthDetail.objects.all()
serializer_class = AuthDetailSerializer
#實現和上面一樣的結果
class AuthDetail_detailView(RetrieveUpdateAPIView) # 查詢單條和更新
class AuthDetail_detailView(RetrieveDestroyAPIView) # 查詢單條和刪除
class AuthDetail_detailView(UpdateAPIView,DestroyAPIView) # 更新和刪除
# 這種方法以後是用的最多的,因為可以重寫一些方法:
# 有可能要重寫--》get_queryset--》get_serializer_class--》perform_create--》get,post方法
1.7.4 編寫視圖函數第五種方法
5個接口都使用一個視圖類:ModelViewSet
,但是需要修改路由。
views.py
from rest_framework.viewsets import ModelViewSet
class AuthDetailView(ModelViewSet): # 5個接口
queryset = AuthDetail.objects.all()
serializer_class = AuthDetailSerializer
urls.py
from django.contrib import admin
from django.urls import path
from drfViews import views
#導入DRF的routers模塊
from rest_framework import routers
router = routers.SimpleRouter()
#註冊
router.register('authDetailView',views.AuthDetailView, 'authDetailView')
urlpatterns = [
path('admin/', admin.site.urls),
]
urlpatterns += router.urls
這樣五個接口就可以訪問了。
但有時候,我們只想讓訪問GET
接口,其他的接口不能訪問,就可以使用ReadOnlyModelViewSet
"""views.py"""
from rest_framework.viewsets import ReadOnlyModelViewSet
class AuthDetailView(ReadOnlyModelViewSet):
queryset = AuthDetail.objects.all()
serializer_class = AuthDetailSerializer
"""urls.py"""
from django.contrib import admin
from django.urls import path
from drfViews import views
#導入DRF的routers模塊
from rest_framework import routers
router = routers.SimpleRouter()
#註冊
router.register('authDetailView',views.AuthDetailView, 'authDetailView')
urlpatterns = [
path('admin/', admin.site.urls),
]
urlpatterns += router.urls
使用GET方法訪問時是沒有問題的,但使用POST,PUT,DELETE方法則會報錯
"detail": "Method \"POST\" not allowed."
"detail": "Method \"PUT\" not allowed."
"detail": "Method \"DELETE\" not allowed."
ModelViewSet
和ReadOnlyModelViewSet
之所有能達到這種效果,主要是它們繼承了以下幾個類。
from rest_framework.viewsets import
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
pass
class ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
mixins.ListModelMixin,
GenericViewSet):
pass
"""
CreateModelMixin,
RetrieveModelMixin,
UpdateModelMixin,
DestroyModelMixin,
ListModelMixin,
""" # 這五個就是上面寫的增刪改查的 擴展視圖類
# 而且這兩個類都繼承了GenericViewSet,
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
pass
# 之所有要重寫路由主要是:ViewSetMixin類,使用它就必須要重寫路由。
以後只要想重寫路由必須要繼承ViewSetMixin
類或子類
from rest_framework.viewsets import
ViewSetMixin
ModelViewSet = CreateModelMixin+RetrieveModelMixin+UpdateModelMixin+DestroyModelMixin+ListModelMixin+GenericViewSet
ReadOnlyModelViewSet = RetrieveModelMixin+ListModelMixin+GenericViewSet
GenericViewSet = ViewSetMixin+generics.GenericAPIView
ViewSet = ViewSetMixin + views.APIView
# 只要繼承了ViewSetMixin以子類ModelViewSet,ReadOnlyModelViewSet,GenericViewSet,ViewSet就必須在urls.py里導入routes自動生成路由
1.7.5 以上類的總結:
2. 路由組件
2.1 兩種使用ViewSetMixin的方法
# 方法一:
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin
class TestAPIView(ViewSetMixin,APIView): # ViewSetMixin必須要寫在前面
pass
from rest_framework.viewsets import ViewSet
class TestAPIView(ViewSet):
pass
TestAPIView(ViewSet) == TestAPIView(ViewSetMixin,APIView):
# 方法二:
from rest_framework.generics import GenericAPIView
from rest_framework.viewsets import ViewSetMixin
class TestGenericAPIView(ViewSetMixin,GenericAPIView): # ViewSetMixin必須要寫在前面
pass
from rest_framework.viewsets import GenericViewSet
class TestGenericAPIView(GenericViewSet):
pass
TestGenericAPIView(GenericViewSet) == TestGenericAPIView(ViewSetMixin,GenericAPIView):
只要繼承了ViewSetMixin
視圖類中的方法就可以不用寫成之前的get,post,put,delete
,名字隨意寫,不過要在路由中寫成path('url/',views.類名.as_view({'get':'自己定義的方法','put':'自己定義的方法2'}))
示例:
"""views.py"""
from rest_framework.viewsets import ViewSet
class TestView(ViewSet):
def list(self,request):
return Response("GET方法")
def create(self,request):
return Response("POST方法")
"""urls.py"""
from django.contrib import admin
from django.urls import path
from drfViews import views
urlpatterns = [
path('admin/', admin.site.urls),
path('testView/', views.TestView.as_view({'get':"list",'post':"create"}))
]
實例這種寫法過程:
視圖中的類中繼承了ViewSetMixin類,而ViewSetMixin方法中重新寫了as_view()方法,而重寫的as_veiw()方法用法:
view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
def as_view(cls, actions=None, **initkwargs):
其中actions就是{'get': 'list', 'post': 'create'},如果actions沒有值則會報錯:
"The `actions` argument must be provided when "
"calling `.as_view()` on a ViewSet. For example "
"`.as_view({'get': 'list'})`"
2.2 自動生成路由步驟:
自動生成路由必須要繼承的類或子類:
GenericViewSet + 5個擴展視圖類之一(
CreateModelMixin,
RetrieveModelMixin,
UpdateModelMixin,
DestroyModelMixin,
ListModelMixin,
)
才能自動生成,因為請求要相互對應:
{'get':'list','post':'create','put':'update','delete':'destroy','get':'retrieve'}
# 這就是為什麼ModelViewSet和 ReadOnlyModelViewSet可以自動生成路由,如果沒有繼承這5個擴展視圖類之一,則不能自動生成
自動生成路由步驟:
# 第一步 導入routers
from django.urls import path, include
from rest_framework import routers
# 第二步 實例化:
router = routers.SimpleRouter()
# 第三步 註冊:
router.register('URL地址',views.對應的類名, '別名')
router.register('authDetailView',views.AuthDetailView, 'authDetailView')
# 第四步:
urlpatterns += router.urls
或
urlpatterns = [
path('URL地址', include(router.urls) )
]
2.3 action再生成路由
from rest_framework.viewsets import GenericViewSet
from rest_framework.decorators import action
class TestView(GenericViewSet):
@action(methods=['GET', 'POST'], detail=False)
def login(self,request):
return Response("GET方法")
def test(self,request):
return Response("POST方法")
action(methods=None, detail=None, url_path=None, url_name=None, **kwargs):
methods = 請求方式,是個列表
detail = 是否帶id, False為不帶
url_path= url地址,不寫則默認方法名為地址
url_name = 別名
給上面的login加上action裝飾器後,
會再之前//172.0.0.1:8000/test/的後面生成路徑--->://172.0.0.1:8000/test/login/
訪問的時候直接訪問//172.0.0.1:8000/test/login/,由於方法里methods=['GET', 'POST']寫了get和post方法,所以get和post方法都會執行。
如果設置url_path
@action(methods=['GET', 'POST'], detail=False,url_path='hello')
def login(self,request):
則訪問的時候就要訪問:
//172.0.0.1:8000/test/hello/
如果 detail=True(方法不常用), 則訪問路徑: //172.0.0.1:8000/test/pk值/login/
代碼:
"""
from rest_framework.viewsets import GenericViewSet
from rest_framework.decorators import action
class TestView(GenericViewSet):
@action(methods=['GET', 'POST'], detail=True)
def login(self,request,pk):
return Response("GET方法")
def test(self,request):
return Response("POST方法")
"""
路由:urls.py
"""
from django.contrib import admin
from django.urls import path, include
from drfViews import views
from rest_framework import routers
router = routers.DefaultRouter()
router.register('test',views.TestView,'test')
urlpatterns = [
path('admin/', admin.site.urls),
path('api/',include(router.urls))
]
"""
上面把自動成生的路由放在了api裏面所以訪問地址:
//127.0.0.1:8000/api/test/login/