首先按照restful規範我們建立一些api接口,按照下面這些形式寫吧:前端
Courses --- GET ---> 查看數據----->返回全部數據列表[{},{},]java
Courses--- POST --->添加數據 -----> 返回添加的數據{ }python
courses/1 ---PUT---> 更新pk=1的數據 ----->返回更新後的數據{ }linux
courses/1 --- DELETE---> 刪除pk=1的數據 -----> 返回空git
courses/1 --- GET --->查看單條數據 -----> 返回單條數據 { }數據庫
這樣,咱們先看一個drf給咱們提供的一個相似於Postman功能的頁面,首先咱們建立一個django項目,建立一個Course表,而後添加一些數據,而後按照下面的步驟操做,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 #引用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')
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
上面的數據,咱們經過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提供的序列化組件。api
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)
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序列化以後的數據,那麼客戶端拿到的是一個有格式的數據,再也不是一行顯示了
看效果:
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對象的啊?