本節目錄
首先按照restful規范咱們創建一些api接口,按照下面這些形式寫吧:
Courses --- GET ---> 查看數據----->返回所有數據列表[{},{},]
Courses--- POST --->添加數據 -----> 返回添加的數據{ }
courses/1 ---PUT---> 更新pk=1的數據 ----->返回更新后的數據{ }
courses/1 --- DELETE---> 刪除pk=1的數據 -----> 返回空
courses/1 --- GET --->查看單條數據 -----> 返回單條數據 { }
這樣,我們先看一個drf給我們提供的一個類似於Postman功能的頁面,首先我們創建一個django項目,創建一個Course表,然后添加一些數據,然后按照下面的步驟操作,
第一步:引入drf的Response對象
from django.shortcuts import render,HttpResponse,redirect import json from django.views import View from app01 import models from rest_framework.views import APIView #引用drf提供的Response對象 from rest_framework.response import Response
#寫我們的CBV視圖 class CourseView(APIView): #返回所有的Course數據 def get(self,request): course_obj_list = models.Course.objects.all() ret = [] for course_obj in course_obj_list: ret.append({ "title":course_obj.title, "desc":course_obj.desc, }) # return HttpResponse(json.dumps(ret, ensure_ascii=False)) return Response(json.dumps(ret, ensure_ascii=False)) #這里使用Response來返回消息 def post(self,request): print(request.data) return HttpResponse('POST')
第二步:配置App,在我們的settings配置文件中配置
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01.apps.App01Config', 'rest_framework', #將它注冊成App ]
第三步,配置我們的路由
""" from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^courses/', views.CourseView.as_view(),name='courses'), ]
第四步:啟動項目,通過瀏覽器訪問我們的路由(必須是瀏覽器訪問才能看到對應的功能),看效果:
這里面我們可以發送不同類型的請求,看到對應的返回數據,類似於Postman,但是沒有Postman好用,所以以后調試我們還是用Postman工具,但是我們知道一下昂。
上面的數據,我們通過json自己進行的序列化,其實django也給我們提供了一個簡單的序列化組件,看用法:
from django.shortcuts import render,HttpResponse,redirect import json from django.views import View from app01 import models from rest_framework.views import APIView from django.core.serializers import serialize #django的序列化組件,不是我們要學的drf的序列化組件昂 #不用json自己來序列化了,太麻煩,我們使用drf提供的序列化組件 from rest_framework.response import Response class CourseView(APIView): def get(self,request): course_obj_list = models.Course.objects.all() # ret = [] # for course_obj in course_obj_list: # ret.append({ # "title":course_obj.title, # "desc":course_obj.desc, # }) # return HttpResponse(json.dumps(ret, ensure_ascii=False)) # return Response(json.dumps(ret, ensure_ascii=False) se_data = serialize('json',course_obj_list,ensure_ascii=False) print(se_data)#也拿到了序列化之后的數據,簡潔很多 #[{"model": "app01.course", "pk": 1, "fields": {"title": "python", "desc": "666"}}, {"model": "app01.course", "pk": 2, "fields": {"title": "linux", "desc": "\u4e5f\u5f88\u597d"}}, {"model": "app01.course", "pk": 3, "fields": {"title": "go", "desc": "\u5c06\u6765\u53ef\u80fd\u5f88\u597d"}}] return Response(se_data)
那么我們知道了兩個序列化方式了,這個序列化是不是就簡單很多啊,但是drf給我們做了一個更牛逼的序列化組件,功能更強大,而且不僅僅能做序列化,還能做其他的事情,所以呢,做api的時候,我們還是用drf提供的序列化組件。

import json from datetime import datetime from datetime import date #對含有日期格式數據的json數據進行轉換 class JsonCustomEncoder(json.JSONEncoder): def default(self, field): if isinstance(field,datetime): return field.strftime('%Y-%m-%d %H:%M:%S') elif isinstance(field,date): return field.strftime('%Y-%m-%d') else: return json.JSONEncoder.default(self,field) d1 = datetime.now() dd = json.dumps(d1,cls=JsonCustomEncoder) print(dd)
接下來重點到了,我們玩一下drf提供的數據序列化組件:
1.我們通過GET方法,來查看所有的Course數據。
from django.shortcuts import render,HttpResponse,redirect import json from django.views import View from app01 import models from rest_framework.views import APIView from django.core.serializers import serialize #django的序列化組件,不是我們要學的drf的序列化組件昂
#from rest_framework import status #返回指定狀態碼的時候會用到
#return Response(se_data,status=status=HTTP_400_BAD_REQUEST)
#或者這種方式返回來指定狀態碼:return JsonResponse(serializer.data, status=201)
from rest_framework.response import Response # 序列化方式3,1.引入drf序列化組件 from rest_framework import serializers # 2.首先實例化一個類,繼承drf的serializers.Serializer,類似於我們的form組件和models的用法 class CourseSerializers(serializers.Serializer): #這里面也要寫對應的字段,你寫了哪些字段,就會對哪些字段的數據進行序列化,沒有被序列化的字段,不會有返回數據,你可以注釋掉一個,然后看返回的數據是啥 title = serializers.CharField(max_length=32,required=False) #序列化的時候還能校驗字段 desc = serializers.CharField(max_length=32) class CourseView(APIView): def get(self,request): course_obj_list = models.Course.objects.all() # 3.使用我們創建的序列化類 cs = CourseSerializers(course_obj_list, many=True) # 序列化多個對象的時候,需要些many=True參數 #4.通過返回對象的data屬性就能拿到序列化之后的數據 se_data = cs.data print(se_data) #[OrderedDict([('title', 'python'), ('desc', '666')]), OrderedDict([('title', 'linux'), ('desc', '也很好')]), OrderedDict([('title', 'go'), ('desc', '將來可能很好')])] 列表嵌套的有序字典。 #還記得創建字典的另外一種寫法嗎?這個沒啥用昂,給大家回顧一下之前的知識 # d1 = {'name':'chao'} # d2 = dict([('name','chao'),('age',18)]) # print(d1) #{'name': 'chao'} # print(d2) #{'age': 18, 'name': 'chao'} # # 有序字典 # from collections import OrderedDict # d3 = OrderedDict([('name','Jaden'),('age',22)]) # print(d3) #OrderedDict([('name', 'Jaden'), ('age', 22)]) return Response(se_data) #drf的Response如果返回的是drf序列化之后的數據,那么客戶端拿到的是一個有格式的數據,不再是一行顯示了
看效果:
2.通過POST方法來添加一條數據:
from django.shortcuts import render,HttpResponse,redirect from django.views import View from app01 import models from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import serializers class CourseSerializers(serializers.Serializer): title = serializers.CharField(max_length=32) desc = serializers.CharField(max_length=32) class CourseView(APIView): def get(self,request): course_obj_list = models.Course.objects.all() cs = CourseSerializers(course_obj_list, many=True) se_data = cs.data return Response(se_data) def post(self,request): # print(request.data) #{'desc': 'java也挺好', 'title': 'java'} #發送過來的數據是不是要進行驗證啊,drf的序列化組件還能校驗數據 cs = CourseSerializers(data=request.data,many=False) #注意必須是data=這種關鍵字參數,注意,驗證單條數據的時候寫上many=False參數,而且我們還要序列化這個數據,因為我們要給客戶端返回這個數據 # print(cs.is_valid()) #True ,如果少數據,得到的是False if cs.is_valid(): print(cs.data) models.Course.objects.create(**cs.data)#添加數據 return Response(cs.data) #按照post添加數據的api規則,咱們要返回正確的數據 else: # 假如客戶端發送過來的數據是這樣的,少title的數據 # { # "desc": "java也挺好" # } cs_errors = cs.errors # print(cs_errors) #{'title': ['This field is required.']} return Response(cs_errors) # postman上我們看到的效果是下面這樣的 # { # "title": [ # "This field is required." # ] # }
然后添加一些數據,好,接下來我們玩一些有關聯關系的表
class Author(models.Model): nid = models.AutoField(primary_key=True) name=models.CharField( max_length=32) age=models.IntegerField() class AuthorDetail(models.Model): nid = models.AutoField(primary_key=True) birthday=models.DateField() telephone=models.BigIntegerField() addr=models.CharField( max_length=64) class Publish(models.Model): nid = models.AutoField(primary_key=True) name=models.CharField( max_length=32) city=models.CharField( max_length=32) email=models.EmailField() class Book(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField( max_length=32) publishDate=models.DateField() price=models.DecimalField(max_digits=5,decimal_places=2) publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE) #多對一到Publish表 authors=models.ManyToManyField(to='Author',) #多對多到Author表
看序列化代碼:
from django.shortcuts import render,HttpResponse,redirect from django.views import View from app01 import models from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import serializers class BookSerializers(serializers.Serializer): #我們先序列化寫兩個字段的數據,別忘了這里面的字段和model表中的字段變量名要一樣 title = serializers.CharField(max_length=32) price = serializers.DecimalField(max_digits=5, decimal_places=2) #一對多的處理 # publish = serializers.CharField(max_length=32) #返回對象 publish_email = serializers.CharField(max_length=32, source='publish.email') # source指定返回的多對一的那個publish對象的email數據,並且我們現在找到書籍的email,所以前面的字段名稱就可以不和你的publish對應好了,隨便取名字 publish_name = serializers.CharField(max_length=32, source='publish.name') # source指定返回的多對一的那個publish對象的其他字段數據,可以接着寫字段,也就是說關聯的所有的字段的數據都可以寫在這里進行序列化 #對多對的處理 # authors = serializers.CharField(max_length=32) #bookobj.authors拿到的類似於一個models.Authors.object,打印的時候這是個None # authors = serializers.CharField(max_length=32,source="authors.all") #這樣寫返回的是queryset類型的數據,這樣給前端肯定是不行的,所以按照下面的方法寫 authors = serializers.SerializerMethodField() #序列化方法字段,專門給多對多字段用的,然后下面定義一個方法,方法名稱寫法是這樣的get_字段名,名字必須是這樣 def get_authors(self,obj): #參數寫一個obj,這個obj是一個一個的書籍對象,然后我們通過書籍對象來返回對應的數據 # author_list_values = obj.authors.all().values() #返回這樣類型的數據也行,那么具體你要返回什么結構的數據,需要和前端人員溝通清楚,然后這里對數據進行加工 #假如加工成的數據是這種類型的[ {},{} ],就可以按照下面的邏輯來寫,我簡單寫的,肯定有更好的邏輯來加工這些數據 author_list_values = [] author_dict = {} author_list = obj.authors.all() for i in author_list: author_dict['name'] = i.name author_list_values.append(author_dict) return author_list_values class BookView(APIView): def get(self,request): book_obj_list = models.Book.objects.all() s_books = BookSerializers(book_obj_list,many=True) return Response(s_books.data) def post(self,request): pass
其實serializer在內部就做了這點事兒,偽代碼昂。
urls.py是這樣寫的:
urlpatterns = [ #url(r'^admin/', admin.site.urls), #做一些針對書籍表的接口 url(r'^books/', views.BookView.as_view(),), ]
然后看Postman返回的數據:
那么我們就能夠完成各種數據的序列化了,但是你會發現,這樣寫太累啦,這只是一張表啊,要是上百張表咋整啊,所以還有一個更簡單的方式(類似於form和modelform的區別)。
我們使用ModelSerializer,看代碼:
#ModelSerializer class BookSerializers(serializers.ModelSerializer): class Meta: model=models.Book # fields=['title','price','publish','authors'] fields = "__all__" # 如果直接寫all,你拿到的數據是下面這樣的,但是如果人家前端和你要的作者的id和名字,你是不是要處理一下啦 # [ # { # "nid": 3, # "title": "go", # "publishDate": null, # "price": "122.00", # "publish": 2, # "authors": [ # 2, # 1 # ] # } # ] #那么沒辦法,只能自己再進行加工處理了,按照之前的方式 authors = serializers.SerializerMethodField() def get_authors(self,obj): author_list_values = [] author_dict = {} author_list = obj.authors.all() for i in author_list: author_dict['id'] = i.pk author_dict['name'] = i.name author_list_values.append(author_dict) return author_list_values #這個數據就會覆蓋上面的序列化的authors字段的數據 # 那么前端拿到的數據就這樣了 # [ # { # "nid": 3, # "authors": [ # { # "name": "chao", # "id": 1 # }, # { # "name": "chao", # "id": 1 # } # ], # "title": "go", # "publishDate": null, # "price": "122.00", # "publish": 2 # } # ] # 那如果一對多關系的那個publish,前端想要的數據是名字怎么辦呢?還是老辦法,source # publish_name = serializers.CharField(max_length=32, source='publish.name')#但是你會發現序列化之后的數據有個publish:1對應個id值,如果我不想要他怎么辦,那么可以起個相同的變量名來覆蓋它,比如下面的寫法 publish = serializers.CharField(max_length=32, source='publish.name') class BookView(APIView): def get(self,request): book_obj_list = models.Book.objects.all() s_books = BookSerializers(book_obj_list,many=True) return Response(s_books.data) def post(self,request): pass
上面我們完成了get請求來查看所有的書籍信息,接下來我們玩一個post請求添加一條book數據,直接上代碼吧:
class BookSerializers(serializers.ModelSerializer): class Meta: model=models.Book fields = "__all__" # 注意先把下面這些注釋掉,不然由於get和post請求我們用的都是這個序列化組件,會出現多對多變量沖突的問題,所以一般都將讀操作和寫操作分成兩個序列化組件來寫 # authors = serializers.SerializerMethodField() #也可以用來處理一對多的關系字段 # def get_authors(self,obj): # author_list_values = [] # author_dict = {} # author_list = obj.authors.all() # for i in author_list: # author_dict['id'] = i.pk # author_dict['name'] = i.name # author_list_values.append(author_dict) # return author_list_values # publish = serializers.CharField(max_length=32, source='publish.name') class BookView(APIView): def get(self,request): book_obj_list = models.Book.objects.all() s_books = BookSerializers(book_obj_list,many=True) return Response(s_books.data) def post(self,request): b_serializer = BookSerializers(data=request.data,many=False) if b_serializer.is_valid(): print('xxxx') b_serializer.save() #因為這個序列化器我們用的ModelSerializer,並且在BookSerializers類中我們指定了序列化的哪個表,所以直接save,它就知道我們要將數據保存到哪張表中,其實這句話執行的就是個create操作。 return Response(b_serializer.data) #b_serializer.data這就是個字典數據 else: return Response(b_serializer.errors)
上面我們完成了GET和POST請求的接口寫法,下面我們來完成PUT、DELETE、GET查看單條數據的幾個接口。

#一個讀序列化組件,一個寫序列化組件 class BookSerializers1(serializers.ModelSerializer): class Meta: model=models.Book fields = "__all__" def create(self, validated_data): print(validated_data) #{'publishDate': datetime.date(2012, 12, 12), 'publish': <Publish: Publish object>, 'authors': [<Author: Author object>, <Author: Author object>], 'title': '老酒3', 'price': Decimal('15.00')} authors = validated_data.pop('authors') obj = models.Book.objects.create(**validated_data) obj.authors.add(*authors) return obj class BookSerializers2(serializers.ModelSerializer): class Meta: model=models.Book fields = "__all__" authors = serializers.SerializerMethodField() def get_authors(self,obj): print('sssss') author_list_values = [] author_dict = {} author_list = obj.authors.all() for i in author_list: author_dict['id'] = i.pk author_dict['name'] = i.name author_list_values.append(author_dict) return author_list_values publish = serializers.CharField(max_length=32, source='publish.name') class BookView(APIView): def get(self,request): book_obj_list = models.Book.objects.all() s_books = BookSerializers2(book_obj_list,many=True) return Response(s_books.data) def post(self,request): b_serializer = BookSerializers1(data=request.data,many=False) if b_serializer.is_valid(): print('xxxx') b_serializer.save() return Response(b_serializer.data) else: return Response(b_serializer.errors)
urls.py內容如下:
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), # url(r'^courses/', views.CourseView.as_view()), #做一些針對書籍表的接口 #GET和POST接口的url url(r'^books/$', views.BookView.as_view(),), #別忘了$符號結尾 #PUT、DELETE、GET請求接口 url(r'^books/(\d+)/', views.SBookView.as_view(),), ]
views.py代碼如下:
from django.shortcuts import render,HttpResponse,redirect from django.views import View from app01 import models from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import serializers class BookSerializers(serializers.ModelSerializer): class Meta: model=models.Book fields = "__all__" class BookView(APIView): def get(self,request): ''' 查看所有書籍 :param request: :return: ''' book_obj_list = models.Book.objects.all() s_books = BookSerializers(book_obj_list,many=True) return Response(s_books.data) def post(self,request): ''' 添加一條數據 :param request: :return: ''' b_serializer = BookSerializers(data=request.data,many=False) if b_serializer.is_valid(): b_serializer.save() return Response(b_serializer.data) else: return Response(b_serializer.errors) #因為更新一條數據,刪除一條數據,獲取一條數據,都有個單獨的參數(獲取一條數據的,一般是id,所以我將put、delete、get寫到了一個視圖類里面,也就是說結合上面那個BookView視圖類,完成了我們的那些接口) class SBookView(APIView): def get(self,request,id): ''' 獲取單條數據 :param request: :param id: :return: ''' book_obj = models.Book.objects.get(pk=id)#獲取這條數據對象 #接下來序列化單個model對象,序列化單個對象返回的是一個字典結構 {},序列化多個對象返回的是[{},{}]這種結構 book_serializer = BookSerializers(book_obj,many=False) return Response(book_serializer.data) def put(self,request,id): ''' 更新一條數據 :param request:request.data更新提交過來的數據 :param id: :return: ''' book_obj = models.Book.objects.get(pk=id) b_s = BookSerializers(data=request.data,instance=book_obj,many=False) #別忘了寫instance,由於我們使用的ModelSerializer,所以前端提交過來的數據必須是所有字段的數據,當然id字段不用 if b_s.is_valid(): b_s.save() #翻譯成的就是update操作 return Response(b_s.data) #接口規范要求咱們要返回更新后的數據 else: return Response(b_s.errors) def delete(self,request,id): ''' 刪除一條數據 :param request: :param id: :return: ''' book_obj = models.Book.objects.get(pk=id).delete() return Response("") #別忘了接口規范說最好返回一個空
好,五個接口寫完,咱們的序列化組件就算是講完了,別忘了看這一節最后的那個坑。
重寫save的create方法
class BookSerializers(serializers.ModelSerializer): class Meta: model=Book fields="__all__" # exclude = ['authors',] # depth=1 def create(self, validated_data): authors = validated_data.pop('authors') obj = Book.objects.create(**validated_data) obj.authors.add(*authors) return obj
超鏈接API,Hyperlinked
class BookSerializers(serializers.ModelSerializer): publish= serializers.HyperlinkedIdentityField( view_name='publish_detail', lookup_field="publish_id", lookup_url_kwarg="pk") class Meta: model=Book fields="__all__" #depth=1
serializer的屬性和方法:
1.save() 在調用serializer.save()時,會創建或者更新一個Model實例(調用create()或update()創建),具體根據序列化類的實現而定,如: 2.create()、update() Serializer中的create()和update()方法用於創建生成一個Model實例,在使用Serializer時,如果要保存反序列化后的實例到數據庫,則必須要實現這兩方法之一,生成的實例則作為save()返回值返回。方法屬性validated_data表示校驗的傳入數據,可以在自己定義的序列化類中重寫這兩個方法。 3. is_valid() 當反序列化時,在調用Serializer.save()之前必須要使用is_valid()方法進行校驗,如果校驗成功返回True,失敗則返回False,同時會將錯誤信息保存到serializer.errors屬性中。 4.data serializer.data中保存了序列化后的數據。 5.errors 當serializer.is_valid()進行校驗后,如果校驗失敗,則將錯誤信息保存到serializer.errors屬性中。
serializer的Field:
1.CharField 對應models.CharField,同時如果指定長度,還會負責校驗文本長度。 max_length:最大長度; min_length:最小長度; allow_blank=True:表示允許將空串做為有效值,默認False; 2.EmailField 對應models.EmailField,驗證是否是有效email地址。 3.IntegerField 對應models.IntegerField,代表整數類型 4.FloatField 對應models.FloatField,代表浮點數類型 5.DateTimeField 對應models.DateTimeField,代表時間和日期類型。 format='YYYY-MM-DD hh:mm':指定datetime輸出格式,默認為DATETIME_FORMAT值。 需要注意,如果在 ModelSerializer 和HyperlinkedModelSerializer中如果models.DateTimeField帶有auto_now=True或者auto_add_now=True,則對應的serializers.DateTimeField中將默認使用屬性read_only=True,如果不想使用此行為,需要顯示對該字段進行聲明: class CommentSerializer(serializers.ModelSerializer): created = serializers.DateTimeField() class Meta: model = Comment 6.FileField 對應models.FileField,代表一個文件,負責文件校驗。 max_length:文件名最大長度; allow_empty_file:是否允許為空文件; 7.ImageField 對應models.ImageField,代表一個圖片,負責校驗圖片格式是否正確。 max_length:圖片名最大長度; allow_empty_file:是否允許為空文件; 如果要進行圖片處理,推薦安裝Pillow: pip install Pillow 8.HiddenField 這是serializers中特有的Field,它不根據用戶提交獲取值,而是從默認值或可調用的值中獲取其值。一種常見的使用場景就是在Model中存在user_id作為外鍵,在用戶提交時,不允許提交user_id,但user_id在定義Model時又是必須字段,這種情況下就可以使用HiddenField提供一個默認值: class LeavingMessageSerializer(serializers.Serializer): user = serializers.HiddenField( default=serializers.CurrentUserDefault() )
serializer的公共參數:
所謂公共參數,是指對於所有的serializers.<FieldName>都可以接受的參數。以下是常見的一些公共參數。 1.read_only read_only=True表示該字段為只讀字段,即對應字段只用於序列化時(輸出),而在反序列化時(創建對象)不使用該字段。默認值為False。 2.write_only write_only=True表示該字段為只寫字段,和read_only相反,即對應字段只用於更新或創建新的Model時,而在序列化時不使用,即不會輸出給用戶。默認值為False。 3.required required=False表示對應字段在反序列化時是非必需的。在正常情況下,如果反序列化時缺少字段,則會拋出異常。默認值為True。 4.default 給字段指定一個默認值。需要注意,如果字段設置了default,則隱式地表示該字段已包含required=False,如果同時指定default和required,則會拋出異常。 5.allow_null allow_null=True表示在序列化時允許None作為有效值。需要注意,如果沒有顯式使用default參數,則當指定allow_null=True時,在序列化過程中將會默認default=None,但並不會在反序列化時也默認。 6.validators 一個應用於傳入字段的驗證函數列表,如果驗證失敗,會引發驗證錯誤,否則直接是返回,用於驗證字段,如: username = serializers.CharField(max_length=16, required=True, label='用戶名', validators=[validators.UniqueValidator(queryset=User.objects.all(),message='用戶已經存在')]) 7.error_message 驗證時錯誤碼和錯誤信息的一個dict,可以指定一些驗證字段時的錯誤信息,如: mobile= serializers.CharField(max_length=4, required=True, write_only=True, min_length=4, label='電話', error_messages={ 'blank': '請輸入驗證碼', 'required': '該字段必填項', 'max_length': '驗證碼格式錯誤', 'min_length': '驗證碼格式錯誤', }) 7.style 一個鍵值對,用於控制字段如何渲染,最常用於對密碼進行密文輸入,如: password = serializers.CharField(max_length=16, min_length=6, required=True, label='密碼', error_messages={ 'blank': '請輸入密碼', 'required': '該字段必填', 'max_length': '密碼長度不超過16', 'min_length': '密碼長度不小於6', }, style={'input_type': 'password'}, write_only=True) 9.label 一個簡短的文本字串,用來描述該字段。 10.help_text 一個文本字串,可用作HTML表單字段或其他描述性元素中字段的描述。 11.allow_blank allow_blank=True 可以為空 設置False則不能為空 12.source source='user.email'(user表的email字段的值給這值) 設置字段值 類似default 通常這個值有外鍵關聯屬性可以用source設置 13.validators 驗證該字段跟 單獨的validate很像 UniqueValidator 單獨唯一 validators=[UniqueValidator(queryset=UserProfile.objects.all()) UniqueTogetherValidator: 多字段聯合唯一,這個時候就不能單獨作用於某個字段,我們在Meta中設置。 validators = [UniqueTogetherValidator(queryset=UserFav.objects.all(),fields=('user', 'course'),message='已經收藏')] 14.error_messages 錯誤消息提示 error_messages={ "min_value": "商品數量不能小於一", "required": "請選擇購買數量" }) 7.ModelSerializers ModelSerializers繼承於Serializer,相比其父類,ModelSerializer自動實現了以下三個步驟: 1.根據指定的Model自動檢測並生成序列化的字段,不需要提前定義; 2.自動為序列化生成校驗器; 3.自動實現了create()方法和update()方法。 使用ModelSerializer方式如下: class StudentSerializer(serializers.ModelSerializer): class Meta: # 指定一個Model,自動檢測序列化的字段 model = StudentSerializer fields = ('id', 'name', 'age', 'birthday') 相比於Serializer,可以說是簡單了不少,當然,有時根據項目要求,可能也會在ModelSerializer中顯示聲明字段,這些在后面總結。 model 該屬性指定一個Model類,ModelSerializer會根據提供的Model類自動檢測出需要序列化的字段。默認情況下,所有Model類中的字段將會映射到ModelSerializer類中相應的字段。
關於同一個序列化組件在做get(獲取數據)和post(添加數據)時候的一些坑,直接上代碼吧(等我再深入研究一下,再給出更好的答案~~):
class BookSerializers(serializers.ModelSerializer): class Meta: model=models.Book fields = "__all__" # 下面這個extra_kwargs暫時忽略 # extra_kwargs = { # # 'publish': {'write_only': True}, #讓publish和authors字段的數據只往數據庫里面寫,但是查詢展示的時候,不顯示這兩個字段,因為我們下面配置了publish要返回的數據叫做publish_name # # 'authors': {'write_only': True} # }
#read_only屬性的意思是,這個字段名稱的數據只能查看,不保存,如果用戶提交的數據中有這個字段的數據,將會被剔除。 #在我們的BookSerializers類下面可以重寫create和update方法,但是validated_data這個數據是在用戶提交完數據過來,並且經過序列化校驗之后的數據,序列化校驗除了一些required等基礎校驗之外,還會會根據咱們寫的這個序列化組件中設置的字段中有read_only=True屬性的字段排除掉,這也是為什么我們在面寫多對多和一對多字段時,如果字段名稱和model表中多對多或者一對多的字段名稱相同,那么用戶提交過來的數據中以這個字段命名的數據會被剔除,那么validated_data里面就沒有多對多和一對多字段的數據了,那么再執行create方法的時候validated_data.pop('authors')這里就會報錯,說找不到authors屬性。 # def create(self, validated_data): # print(validated_data) # authors = validated_data.pop('authors') # for i in authors: # print(i.pk) # obj = models.Book.objects.create(**validated_data) # obj.authors.add(*authors) # return obj authors_list = serializers.SerializerMethodField() #注意,當你用這個序列化組件既做查詢操作,又做添加數據的操作,那么這個字段的名字不能和你models中多對多字段的名字相同,這里也就不能叫做authors # authors = serializers.SerializerMethodField() # authors_list = A() #報錯:{"authors_list": ["This field is required."]},也就是說,如果我們將SerializerMethodField中的read_only改成False,那么在進行字段驗證的時候,這個字段就沒有被排除,也就是說,必須傳給我這個authors_list名字的數據,但是如果我們前端給的數據中添加了這么一個數據authors_list:[1,2]的話,你會發現還是會報錯,.is_valid()這里報錯了,為什么呢,因為,序列化組件校驗的時候,在model表中找不到一個叫做authors_list的字段,所以還是報錯,所以,在這里有個辦法就是將這個序列化組件中的這個字段改個名字,不能和authors名字一樣,並且使用默認配置(也就是read_only=true) # def get_authors_list(self,obj): def get_authors_list(self,obj): author_list_values = [] author_list = obj.authors.all() for i in author_list: author_dict = {} author_dict['id'] = i.pk author_dict['name'] = i.name author_list_values.append(author_dict) return author_list_values # publish = serializers.CharField(max_length=32, source='publish.name',read_only=True) #如果這個字段名字和數據表中外鍵字段名稱相同,並且設置了read_only=True屬性,那么當用戶提交數據到后端保存的時候,就會報錯NOT NULL constraint failed: app01_book.publish_id,1.要么你將這個名字改成別的名字,2.要么去數據庫表中將這個字段設置一個null=True,但是第二種方式肯定是不太好的,記住,當你獲取數據時,使用這個序列化組件,即便是這個字段的名字和數據表中字段名字相同,也是沒有問題的,只有在用戶提交數據保存的時候才會有問題,所以最好的解決方式就是加read_only屬性,並且改一下字段名字,不要和數據表中這個字段的名字相同 publish_name = serializers.CharField(max_length=32, source='publish.name',read_only=True)
按照我們上面的序列化組件的視圖,接着寫,我們上面只說了一個Book表的幾個接口操作,但是我們是不是還有其他表呢啊,如果我們將上面的四個表都做一些序列化的接口操作,我們是不是按照下面的方式寫啊
from rest_framework.views import APIView from rest_framework.response import Response from .models import * from django.shortcuts import HttpResponse from django.core import serializers from rest_framework import serializers class BookSerializers(serializers.ModelSerializer): class Meta: model=Book fields="__all__" #depth=1 class PublshSerializers(serializers.ModelSerializer): class Meta: model=Publish fields="__all__" depth=1 class BookViewSet(APIView): def get(self,request,*args,**kwargs): book_list=Book.objects.all() bs=BookSerializers(book_list,many=True,context={'request': request}) return Response(bs.data) def post(self,request,*args,**kwargs): print(request.data) bs=BookSerializers(data=request.data,many=False) if bs.is_valid(): print(bs.validated_data) bs.save() return Response(bs.data) else: return HttpResponse(bs.errors) class BookDetailViewSet(APIView): def get(self,request,pk): book_obj=Book.objects.filter(pk=pk).first() bs=BookSerializers(book_obj,context={'request': request}) return Response(bs.data) def put(self,request,pk): book_obj=Book.objects.filter(pk=pk).first() bs=BookSerializers(book_obj,data=request.data,context={'request': request}) if bs.is_valid(): bs.save() return Response(bs.data) else: return HttpResponse(bs.errors) class PublishViewSet(APIView): def get(self,request,*args,**kwargs): publish_list=Publish.objects.all() bs=PublshSerializers(publish_list,many=True,context={'request': request}) return Response(bs.data) def post(self,request,*args,**kwargs): bs=PublshSerializers(data=request.data,many=False) if bs.is_valid(): # print(bs.validated_data) bs.save() return Response(bs.data) else: return HttpResponse(bs.errors) class PublishDetailViewSet(APIView): def get(self,request,pk): publish_obj=Publish.objects.filter(pk=pk).first() bs=PublshSerializers(publish_obj,context={'request': request}) return Response(bs.data) def put(self,request,pk): publish_obj=Publish.objects.filter(pk=pk).first() bs=PublshSerializers(publish_obj,data=request.data,context={'request': request}) if bs.is_valid(): bs.save() return Response(bs.data) else: return HttpResponse(bs.errors)
好,這樣,我們看一下面向對象多繼承的用法:
class Animal: def __init__(self,name,age): self.name = name self.age = age def eat(self): print('吃') def drink(self): print('喝') #eat和drink才是動物共有的,下面三個不是共有的,所以直接這么些就不合適了,所以看下面的寫法,單獨寫一些類,其他一部分動物有的,放到一個類里面,在多繼承 # def eatshit(self): # print('吃s') # def zhiwang(self): # print('織網') # def flying(self): # print('飛') class Eatshit: def eatshit(self): print('吃s') class Zhiwang: def zhiwang(self): print('織網') class Flying: def zhiwang(self): print('織網') class Jumping: def zhiwang(self): print('跳') class Dog(Animal,Eatshit):pass class Spider(Animal,Zhiwang):pass class Bird(Animal,Flying):pass class Daishu(Animal,Flying,Jumping):pass
那好,基於這種繼承形式,我們是不是就要考慮了,我們上面對每個表的那幾個接口操作,大家的處理數據的邏輯都差不多啊,而且你會發現,這么多表,我每個表的GET、PUT、DELETE、POST操作其實都差不多,基本上就兩個地方再發生變化,這里我們稱為兩個變量。
publish_list=Publish.objects.all() #表所有的數據
bs=PublshSerializers(publish_list,many=True,context={'request': request}) #序列化組件
Mixin混合類
關於數據邏輯處理的操作,drf幫我們封裝好了幾個Mixin類,我們來玩一下就行了,看代碼:
from django.shortcuts import render,HttpResponse,redirect from django.views import View from app01 import models from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import serializers #將序列化組件都放到一個單獨的文件里面,然后引入進來 from app01.serializer import BookSerializers,PublishSerializers from rest_framework import generics from rest_framework.mixins import ListModelMixin,CreateModelMixin,UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin # ListModelMixin 查看所有數據,對應着咱們上面的get查看所有數據的接口 # CreateModelMixin 添加數據的操作,封裝了一個create操作,對應着上面的POST添加數據的幾口 # UpdateModelMixin 更新 # DestroyModelMixin 銷毀(刪除) # RetrieveModelMixin 獲取單條數據 # 我們自己提煉出,說,每個表的操作基本都是上面的get、post、delete、put操作,所以我們想將這幾個方法提煉出來,將來供其他類來繼承使用,那么drf幫我們封裝好了,就是這幾個Minin類 class PublishView(ListModelMixin,CreateModelMixin,generics.GenericAPIView): ''' GenericAPIView肯定繼承了APIView,因為APIView里面的功能是我們必須的,而這個GenericAPIView是幫我們做銜接用的,把你的APIView的功能和我們的Minin類的功能銜接、調度起來的 ''' #繼承完了之后,我們需要將我們前面各個表的序列化中提煉的兩個不同的變量告訴咱的類,注意,下面的兩個變量名就是他們倆,不能改,並且必須給 queryset = models.Publish.objects.all() serializer_class = PublishSerializers def get(self,request): ''' 分發找到對應的請求方法,就是咱的get方法,而處理數據的邏輯是繼承的那個ListModelMixin類里面的list方法做了,所以我們只需要return self.list(request方法就行了,處理數據的邏輯就不要我們自己再寫了 :param request: :return: ''' return self.list(request) #list方法幫我們做了序列化 #post方法添加一條數據,我們只需要執行一下CreateModelMixin類中的create方法就行了 def post(self,request): return self.create(request) class SPublishView(UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin,generics.GenericAPIView): #下面這兩個變量和對應數據是必須給的 queryset = models.Publish.objects.all() serializer_class = PublishSerializers # def get(self,request,id):#id就不需要傳了,因為人家要求在url中添加的命名分組的pk參數自動來做了 def get(self,request, *args, **kwargs): #*args, **kwargs是為了接收url的那些參數的,咱們寫的有個pk參數。 return self.retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs): return self.update(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs)
序列化組件的類我們放到了一個單獨的文件中,名字叫做serializer.py,內容如下
from app01 import models from rest_framework import serializers class BookSerializers(serializers.ModelSerializer): class Meta: model=models.Book fields = "__all__" class PublishSerializers(serializers.ModelSerializer): class Meta: model=models.Publish fields = "__all__"
urls.py內容如下:
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [#publish表的接口 url(r'^publishs/$', views.PublishView.as_view(),), # url(r'^publishs/(\d+)/', views.SPublishView.as_view(),), #使用UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin這類Mixin類的時候,人家要求必須有個命名分組參數,名字叫做pk,名字可以改,但是先這樣用昂 url(r'^publishs/(?P<pk>\d+)/', views.SPublishView.as_view(),), ]
玩了這些drf混合類之后,你會發現,處理數據的相同的邏輯部分被省略了,代碼簡化了不少。
但是你看,我們上面只是寫了一個publish表的操作,咱們還有好多其他表呢,他們的操作是不是也是GET、POST、DELETE、PUT等操作啊,所以你想想有沒有優化的地方
####################Author表操作########################## ListCreateAPIView類就幫我們封裝了get和create方法 class AuthorView(generics.ListCreateAPIView): queryset = models.Author.objects.all() serializer_class = AuthorSerializers #RetrieveUpdateDestroyAPIView這個類封裝了put、get、patch、delete方法 class SAuthorView(generics.RetrieveUpdateDestroyAPIView): queryset = models.Author.objects.all() serializer_class = AuthorSerializers
然后你再看,還有優化的地方,上面這兩個類里面的東西是一樣的啊,能不能去重呢,當然可以了,一個類搞定,看寫法
#####################再次封裝的Author表操作########################## from rest_framework.viewsets import ModelViewSet #繼承這個模塊 class AuthorView(ModelViewSet): queryset = models.Author.objects.all() serializer_class = AuthorSerializers
但是url要改一改了,看url的寫法:
#這兩個url用的都是上面的一個類
url(r'^authors/$', views.AuthorView.as_view({"get":"list","post":"create"}),), url(r'^authors/(?P<pk>\d+)/', views.AuthorView.as_view({ 'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy' }),),
然后大家重啟一下自己的程序,通過postman測一下,肯定可以的。
好,那這個東西怎么玩呢?有興趣的,可以去看看源碼~~~
其實源碼中最關鍵的點是這個:
def view(request, *args, **kwargs): self = cls(**initkwargs) # We also store the mapping of request methods to actions, # so that we can later set the action attribute. # eg. `self.action = 'list'` on an incoming GET request. self.action_map = actions # Bind methods to actions # This is the bit that's different to a standard view
#就下面這三句,非常巧妙 for method, action in actions.items(): {'get':'list',} handler = getattr(self, action) #肯定能找到對應的方法list handler = self.list setattr(self, method, handler) #self.get = self.list
后面再執行dispatch方法之后,那個handler = getattr(self,request.method.lower()) #找到的是list方法去執行的,因為self.get等於self.list了,然后執行list方法,返回對應的內容
咱們上面做的都是數據接口,但是還有邏輯接口,比如登陸,像這種數據接口就直接寫個 class Login(APIView):pass這樣來搞就行了,封裝的越簡單,內部邏輯越復雜,自定制來就越復雜,所以關於不同的邏輯,我們就自己單寫。

注意1: #通過self在繼承類之間調用變量,現在是我們通過Dog類繼承的Animal類中調用了Running類中的變量,也就是說如果你在某個類中找不到對應的屬性,有可能在其他類里面放着了 class Animal: x=10 def foo(self): print(self.x) class Running: x = 20 #在Animal類中加一個類變量x,然后把下面繼承的兩個類的順序發生一下變化,你看看會出現什么情況 class Dog(Animal,Running): # class Dog(Running, Animal): pass d = Dog() d.foo() #20 注意2:給函數傳一個字典數據進去,到底這個字典給了下面哪個形參。 def f1(action,**kwargs): print(action) print(kwargs) f1({'name':'chao'}) #結果:{'name': 'chao'} {} # f1(x=1) #報錯:TypeError: f1() missing 1 required positional argument: 'action' f1(1,x=1) #結果: 1 {'x': 1}
然后大家好奇嗎,想不想去看看put\get\delete的操作中,url里面的那個pk命名路由,到底為啥叫pk,並且,它自己在內部怎么通過pk值找到對應的那個更新之前的原來的model對象的啊?