參考博客:https://www.cnblogs.com/yuanchenqi/articles/8719520.html
一、數據序列化的幾種方式
在Django的視圖函數中,我們從數據庫中獲取數據,由以下幾種方式將其轉化為JSON數據:
1.list強轉方式
class PublishView(View): def get(self, request): publish_list = list(Publish.objects.all().values()) return HttpResponse(json.dumps(publish_list))
通過list強轉的方式。
前台接收到的數據:
[{"id": 1, "name": "\u6e05\u534e\u5927\u5b66\u51fa\u7248\u793e", "email": "qh@gmail.com"}, {"id": 2, "name":
"\u5de5\u4e1a\u51fa\u7248\u793e", "email": "gy@gmail.com"}, {"id": 3, "name": "\u90ae\u7535\u51fa\u7248\u793e", "email":
"yd@gmail.com"}, {"id": 4, "name": "\u56db\u5ddd\u6587\u5b66\u51fa\u7248\u793e", "email": "scwx@gmail.com"}]
2.手動封裝方式
class PublishView(View): def get(self, request): publish_list = Publish.objects.all() temp = [] for obj in publish_list: temp.append({ 'id': obj.id, 'name': obj.name, 'email': obj.email }) return HttpResponse(json.dumps(temp))
通過字段拼接的方式。
使用model_to_dict方法來將對象轉換成字典:
class PublishView(View): def get(self, request): from django.forms.models import model_to_dict publish_list = Publish.objects.all() temp = [] for obj in publish_list: temp.append(model_to_dict(obj)) return HttpResponse(json.dumps(temp))
前台接收到的數據:
[{"id": 1, "name": "\u6e05\u534e\u5927\u5b66\u51fa\u7248\u793e", "email": "qh@gmail.com"}, {"id": 2, "name":
"\u5de5\u4e1a\u51fa\u7248\u793e", "email": "gy@gmail.com"}, {"id": 3, "name": "\u90ae\u7535\u51fa\u7248\u793e", "email":
"yd@gmail.com"}, {"id": 4, "name": "\u56db\u5ddd\u6587\u5b66\u51fa\u7248\u793e", "email": "scwx@gmail.com"}]
3.Django提供的序列化
利用django中的序列化組件:·
class PublishView(View): def get(self, request): from django.core import serializers publish_list = Publish.objects.all() ret = serializers.serialize("json", publish_list) return HttpResponse(ret)
前台接收到的數據:
[{"model": "demo.publish", "pk": 1, "fields": {"name": "\u6e05\u534e\u5927\u5b66\u51fa\u7248\u793e", "email":
"qh@gmail.com"}}, {"model": "demo.publish", "pk": 2, "fields": {"name": "\u5de5\u4e1a\u51fa\u7248\u793e", "email":
"gy@gmail.com"}}, {"model": "demo.publish", "pk": 3, "fields": {"name": "\u90ae\u7535\u51fa\u7248\u793e", "email":
"yd@gmail.com"}}, {"model": "demo.publish", "pk": 4, "fields": {"name": "\u56db\u5ddd\u6587\u5b66\u51fa\u7248\u793e",
"email": "scwx@gmail.com"}}]
4.restframework提供的序列化
利用restframework中的serializer:
from rest_framework import serializers class PublishSerializers(serializers.Serializer): name = serializers.CharField() email = serializers.EmailField() class PublishView(View): def get(self, request): publish_list = Publish.objects.all() # 如果序列化queryset,則需要參數many=True ps = PublishSerializers(publish_list, many=True) # 如果是queryset其中一個obj,則不需要many參數 # ps = PublishSerializers(obj) return HttpResponse(ps.data)
這種方式的前提是安裝djangorestframework。
前端接收到的數據:
OrderedDict([('name', '清華大學出版社'), ('email', 'qh@gmail.com')])OrderedDict([('name', '工業出版社'), ('email', 'gy@gmail.com')])OrderedDict([('name', '郵電出版社'), ('email', 'yd@gmail.com')])OrderedDict([('name', '四川文學出版社'), ('email', 'scwx@gmail.com')])
數據是多個有序字典組成的列表。
二、安裝restframe
pip install djangorestframework
三、POST請求的Content-Type
當我們使用postman發送POST請求時,選擇form-data或x-www-form-urlencoded內容類型發送:

對應請求頭中的Content-Type為application/x-www-form-urlencoded。
Django收到請求后,會將body中的數據a=1&b=2轉化為request.POST字典。
<QueryDict: {'a': ['11'], 'b': ['2']}> # django自動幫我們將body中的數據轉換為字典
但是,如果內容類型選擇raw-->Json發送:

對應請求頭中的Content-Type為application/json。
Django收到請求后,不會自動將其轉換為request.POST。
<QueryDict: {}> # 拿到的request.POST是空的
這時,我們通過request.body可以拿到原始的body數據:
b'{"a":"1","b":"2"}'
四、restframe中的APIView
1.視圖函數繼承APIView
在我們寫視圖函數的時候,原來我們繼承的事View類,而要使用restframework的話,我們需要繼承APIView類:
from rest_framework.views import APIView class PublishView(APIView): def get(self, request): pass def post(self, request): pass
2.APIView和View之間的區別
我們看以下APIView的源碼:
class APIView(View): # The following policies may be set at either globally, or per-view. renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES parser_classes = api_settings.DEFAULT_PARSER_CLASSES authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS metadata_class = api_settings.DEFAULT_METADATA_CLASS versioning_class = api_settings.DEFAULT_VERSIONING_CLASS # Allow dependency injection of other settings to make testing easier. settings = api_settings schema = DefaultSchema() @classmethod def as_view(cls, **initkwargs): """ Store the original class on the view function. This allows us to discover information about the view when we do URL reverse lookups. Used for breadcrumb generation. """ if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet): def force_evaluation(): raise RuntimeError( 'Do not evaluate the `.queryset` attribute directly, ' 'as the result will be cached and reused between requests. ' 'Use `.all()` or call `.get_queryset()` instead.' ) cls.queryset._fetch_all = force_evaluation view = super().as_view(**initkwargs) view.cls = cls view.initkwargs = initkwargs # Note: session based authentication is explicitly CSRF validated, # all other authentication is CSRF exempt. return csrf_exempt(view)
我們可以看到,APIView實際上是繼承自View類。並且做了一些擴展。
APIView也有as_view()方法,其中做了一些判斷后,然后調用了父類(View)中的as_view()方法,並且得到返回的view函數引用,最后將這個view函數引用進行了返回(並且去除了csrf)。
但是不同的是,雖然是父類的as_view()方法返回的view函數引用,但是view中調用dispatch函數就不應該是調用的父類的dispatch,而應該是APIView的dispatch()方法。
我們找到APIView類中的dispatch()方法:
def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs # 首先把request使用initialize_request方法封裝成了一個Request類的對象。 request = self.initialize_request(request, *args, **kwargs) # 將封裝后的request替換到self.request中,以后使用的都是Request類的對象,里面封裝了原生的request # 如果要獲取原生request,在這里使用request._request。 self.request = request self.headers = self.default_response_headers # deprecate? try: self.initial(request, *args, **kwargs) # 判斷請求類型,並且返回對應的視圖函數,例如get()和post(),並賦值給handler if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed # 運行請求對應的視圖函數,得到response response = handler(request, *args, **kwargs) except Exception as exc: response = self.handle_exception(exc) # 在finalize_response方法中,針對response做一些處理 self.response = self.finalize_response(request, response, *args, **kwargs) # 返回給客戶端 return self.response
3.使用restframework中的request
既然restframe中的APIView將request進行了封裝,得到一個新的request(Request對象),那么我們如何使用:
# 使用APIView class PublishView(APIView): def get(self, request): pass def post(self, request): # 使用新的request獲取body數據 print(request.data) print(type(request.data)) return HttpResponse("OK")
使用postman發POST請求,數據類型為JSON,數據為{"a":"1","b":"2"}:

后台打印結果:
{'a': '1', 'b': '2'}
<class 'dict'>
說明restframe幫我們將JSON轉換為了字典。這是Django原生View沒有提供的功能。
注意:application/json數據只能使用request.data來取,而application/x-www-form-urlencoded數據可以使用request.POST和request.data來獲取。
同樣的,在get()中也可以通過request.data獲取數據:
# 使用APIView class PublishView(APIView): def get(self, request): print(request.data) print(type(request.data)) return HttpResponse("get...") def post(self, request): pass
打印結果:
{}
<class 'dict'>
可以看到,get請求附帶在url中的數據,新的request.data並沒有值,也就是說restframe的request只幫我們處理了POST請求的值。
如果我們要獲取GET請求的值,我們可以使用:
# 使用APIView class PublishView(APIView): def get(self, request): print(request._request.GET) # 通過新request中封裝的原生request獲取GET print(request.GET) # 新的request也幫我們封裝了一樣的GET return HttpResponse("get...") def post(self, request): pass
五、通過Response返回值
1.使用Response返回值
from rest_framework import serializers from rest_framework.views import APIView from rest_framework.response import Response from .models import Book, Author class BookSerializers(serializers.Serializer): title = serializers.CharField() price = serializers.IntegerField() pub_date = serializers.DateTimeField() # publish = serializers.ForeignKey("Publish", on_delete=models.CASCADE) # authors = serializers.ManyToManyField("Author") class BookView(APIView): def get(self, request): book_list = Book.objects.all() bs = BookSerializers(book_list, many=True) return Response(bs.data) def post(self, request): pass
這里返回值,我們沒使用HttpResponse,而是使用的restframe提供的Response類。
Response類繼承於HttpResponse類。其中擴展了一些功能。
使用postman發GET請求,看返回結果:
[ { "title": "Python3標准庫", "price": 99, "pub_date": null }, { "title": "C標准庫", "price": 55, "pub_date": null }, { "title": "機器學習", "price": 45, "pub_date": null }, { "title": "深度學習指南", "price": 67, "pub_date": null }, { "title": "意志力", "price": 33, "pub_date": null }, { "title": "股市抄手", "price": 23, "pub_date": null } ]
Response幫我們將返回的JSON數據進行了格式化。
如果用瀏覽器請求,可以看到頁面:

六、處理一對多和多對多數據
1.一對多和多對多的處理
前面小節所描述的都是表中的簡單字段,而沒有包含外鍵和多對多的字段,下面就說明一下一對多和多對多字段的處理方法。
class BookSerializers(serializers.Serializer): title = serializers.CharField() price = serializers.IntegerField() pub_date = serializers.DateTimeField() publish = serializers.CharField(source="publish.name") # 如果想顯示email,則改為source="publish.email" authors = serializers.SerializerMethodField() # 多對多字段,使用這個函數配置后面定義的方法來自定義數據 # 定義一個方法,方法名必須是get_authors,SerializerMethodField()會調用get_authors來獲取返回值,並賦值給authors def get_authors(self, obj): temp = [] for obj in obj.authors.all(): # 將authors中該book對應的所有作者名字加入列表,並返回 temp.append(obj.name) return temp class BookView(APIView): def get(self, request): book_list = Book.objects.all() bs = BookSerializers(book_list, many=True) return Response(bs.data) def post(self, request): pass
在一對多字段(外鍵)中,使用source參數來指定想獲取的外表字段。
在多對多字段中,使用 SerializerMethodField() 並配合自定義處理函數,來定義數據怎么組織。
Postman發GET請求,返回結果:
[ { "title": "Python3標准庫", "price": 99, "pub_date": null, "publish": "工業出版社", "authors": [ "leo", "alex" ] }, { "title": "C標准庫", "price": 55, "pub_date": null, "publish": "清華大學出版社", "authors": [ "alex", "Lucy" ] }, { "title": "機器學習", "price": 45, "pub_date": null, "publish": "工業出版社", "authors": [ "leo" ] }, { "title": "深度學習指南", "price": 67, "pub_date": null, "publish": "郵電出版社", "authors": [ "leo", "Lucy" ] }, { "title": "意志力", "price": 33, "pub_date": null, "publish": "四川文學出版社", "authors": [ "Jone", "Lucy" ] }, { "title": "股市抄手", "price": 23, "pub_date": null, "publish": "郵電出版社", "authors": [ "alex", "Jone" ] } ]
可以看到,所有的作者名都以列表的形式放在返回數據中了。
2.一對多的另外一種處理方式
既然多對多可以使用SerializerMethodField() 配合自定義處理函數來獲取所有的值。那么一對多的情況,我們也可以使用這種方式來處理:
class BookSerializers(serializers.Serializer): title = serializers.CharField() price = serializers.IntegerField() pub_date = serializers.DateTimeField() publish = serializers.SerializerMethodField() def get_publish(self, obj): temp = {"name": obj.publish.name, "email": obj.publish.email} return temp authors = serializers.SerializerMethodField() # 多對多字段,使用這個函數配置后面定義的方法來自定義數據 # 定義一個方法,方法名必須是get_authors,SerializerMethodField()會調用get_authors來獲取返回值,並賦值給authors def get_authors(self, obj): temp = [] for obj in obj.authors.all(): # 將authors中該book對應的所有作者名字加入列表,並返回 temp.append(obj.name) return temp class BookView(APIView): def get(self, request): book_list = Book.objects.all() bs = BookSerializers(book_list, many=True) return Response(bs.data) def post(self, request): pass
可以看到,使用這種方式,我們可以同時獲取publish.name和publish.email,並將其放入一個字典中,一起返回。
獲取到的數據:
[ { "title": "Python3標准庫", "price": 99, "pub_date": null, "publish": { "name": "工業出版社", "email": "gy@gmail.com" }, "authors": [ "leo", "alex" ] }, { "title": "C標准庫", "price": 55, "pub_date": null, "publish": { "name": "清華大學出版社", "email": "qh@gmail.com" }, "authors": [ "alex", "Lucy" ] }, { "title": "機器學習", "price": 45, "pub_date": null, "publish": { "name": "工業出版社", "email": "gy@gmail.com" }, "authors": [ "leo" ] }, { "title": "深度學習指南", "price": 67, "pub_date": null, "publish": { "name": "郵電出版社", "email": "yd@gmail.com" }, "authors": [ "leo", "Lucy" ] }, { "title": "意志力", "price": 33, "pub_date": null, "publish": { "name": "四川文學出版社", "email": "scwx@gmail.com" }, "authors": [ "Jone", "Lucy" ] }, { "title": "股市抄手", "price": 23, "pub_date": null, "publish": { "name": "郵電出版社", "email": "yd@gmail.com" }, "authors": [ "alex", "Jone" ] } ]
七、使用ModelSerializer類來處理
除了第五六節中所描述的利用serializers.Serializer來處理,還可以使用serializers.ModelSerializer類。
class BookModelSerializers(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" class BookView(APIView): def get(self, request): book_list = Book.objects.all() bs = BookModelSerializers(book_list, many=True) return Response(bs.data) def post(self, request): pass
得到返回數據:
[ { "id": 1, "title": "Python3標准庫", "price": 99, "pub_date": null, "publish": 2, "authors": [ 1, 2 ] }, { "id": 2, "title": "C標准庫", "price": 55, "pub_date": null, "publish": 1, "authors": [ 2, 4 ] }, { "id": 3, "title": "機器學習", "price": 45, "pub_date": null, "publish": 2, "authors": [ 1 ] }, { "id": 4, "title": "深度學習指南", "price": 67, "pub_date": null, "publish": 3, "authors": [ 1, 4 ] }, { "id": 5, "title": "意志力", "price": 33, "pub_date": null, "publish": 4, "authors": [ 3, 4 ] }, { "id": 6, "title": "股市抄手", "price": 23, "pub_date": null, "publish": 3, "authors": [ 2, 3 ] } ]
可以看到,一對多和多對多字段,默認返回的是主鍵ID。所以我們要單獨對一對多和多對多的字段做處理,和五六節中的方式一樣:
class BookModelSerializers(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" publish = serializers.SerializerMethodField() def get_publish(self, obj): temp = {"name": obj.publish.name, "email": obj.publish.email} return temp authors = serializers.SerializerMethodField() def get_authors(self, obj): temp = [] for obj in obj.authors.all(): # 將authors中該book對應的所有作者名字加入列表,並返回 temp.append(obj.name) return temp class BookView(APIView): def get(self, request): book_list = Book.objects.all() bs = BookModelSerializers(book_list, many=True) return Response(bs.data) def post(self, request): pass
我們在ModelSerializer類中將自定義處理一對多多對多字段的代碼添加在后面。就會按我們自定義的方式來處理數據。
得到結果:
[ { "id": 1, "publish": { "name": "工業出版社", "email": "gy@gmail.com" }, "authors": [ "leo", "alex" ], "title": "Python3標准庫", "price": 99, "pub_date": null }, { "id": 2, "publish": { "name": "清華大學出版社", "email": "qh@gmail.com" }, "authors": [ "alex", "Lucy" ], "title": "C標准庫", "price": 55, "pub_date": null }, { "id": 3, "publish": { "name": "工業出版社", "email": "gy@gmail.com" }, "authors": [ "leo" ], "title": "機器學習", "price": 45, "pub_date": null }, { "id": 4, "publish": { "name": "郵電出版社", "email": "yd@gmail.com" }, "authors": [ "leo", "Lucy" ], "title": "深度學習指南", "price": 67, "pub_date": null }, { "id": 5, "publish": { "name": "四川文學出版社", "email": "scwx@gmail.com" }, "authors": [ "Jone", "Lucy" ], "title": "意志力", "price": 33, "pub_date": null }, { "id": 6, "publish": { "name": "郵電出版社", "email": "yd@gmail.com" }, "authors": [ "alex", "Jone" ], "title": "股市抄手", "price": 23, "pub_date": null } ]
八、使用POST插入數據
前面第五六七節中,我們都在討論如果使用GET方法獲取全量數據(例如Book的全部數據)。
在這節中,我們討論,如果使用POST來向數據庫中插入一條數據。
class BookModelSerializers(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" class BookView(APIView): def get(self, request): book_list = Book.objects.all() bs = BookModelSerializers(book_list, many=True) return Response(bs.data) def post(self, request): bs = BookModelSerializers(data=request.data) if bs.is_valid(): bs.save() return Response(bs.data) else: return Response(bs.errors)
看post()方法,我們使用BookModelSerializers對收到的request.data數據(即POST請求傳過來的JSON數據)進行反序列化,注意參數data=request.data。
bs.is_valid()會檢查傳過來的JSON數據的格式是否滿足BookModelSerializers中定義的格式。這里我們使用的BookModelSerializers的默認格式(即未添加自定義顯示字段):
{ "id": 7, "title": "Python2標准庫", "price": 99, "pub_date": null, "publish": 1, "authors": [ 1, 2 ] }
也就是"publish"字段需要傳遞主鍵ID,而"authors"字段需要傳遞列表。
如果is_valid()返回值為True,則執行bs.save(),這個方法內部實際上調用了BookModelSerializers的父類ModelSerializers中的create()方法,底層就是插入數據庫的操作。插入成功后,再將插入的數據返回給瀏覽器。
如果is_valid()返回值為False,則將錯誤信息返回給瀏覽器。
使用Postman發送數據:
{ "title": "Python3", "price": 99, "pub_date": "2020-01-20 13:03:04", "publish": 1, "authors": [ 3, 4 ] }
得到的返回結果:
{ "id": 14, "title": "Python3", "price": 99, "pub_date": "2020-01-20T13:03:04Z", "publish": 1, "authors": [ 3, 4 ] }
其中id是自增的。
我們查看Book表中多了一條記錄:

並且在book和author的多對多表中,也添加了2條新的數據:

九、自定義插入數據格式
當我們在BookModelSerializers類定義的時候,使用了自定義顯示字段:
class BookModelSerializers(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" publish = serializers.CharField(source="publish.pk")
例如,publish字段,我們自定義顯示其主鍵ID。
此時再按照第八節中的數據格式進行插入會報錯:
AssertionError at /books/ The `.create()` method does not support writable dotted-source fields by default. Write an explicit `.create()` method for serializer `demo.views.BookModelSerializers`, or set `read_only=True` on dotted-source serializer fields.
出現這個錯誤,是因為 ModelSerializer類中的create()方法,不支持我們自定義數據結構。
所以我們要使用自定義結構,必須自己重寫create()方法:
class BookModelSerializers(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" publish = serializers.CharField(source="publish.pk") def create(self, validated_data): print("validated_data:", validated_data) # 添加book表中的新書記錄 book = Book.objects.create(title=validated_data['title'], price=validated_data['price'], pub_date=validated_data['pub_date'], publish_id=validated_data['publish']['pk']) # 添加多對多表中的記錄 book.authors.add(*validated_data['authors']) return book
再次插入數據:
{ "title": "JAVA", "price": 99, "pub_date": "2020-01-20 13:03:04", "publish": 1, "authors": [ 3, 4 ] }
發現成功添加。
book表中記錄:

多對多表中記錄:

十、修改和刪除單條數據
前面討論了獲取book表的全量數據,以及向表中添加一條數據,這節我們討論如何修改一條數據,以及如何刪除一條數據。
1.創建BookDetailView視圖類
查看全量數據和添加數據我們都使用的是BookView視圖類。而對單條數據進行操作,我們單獨創建一個視圖類。
class BookDetailView(APIView): def get(self, request, id): pass def put(self, request, id): pass def delete(self, request, id): pass
其中的get()方法,用來獲取id對應數據。put()方法用來修改id對應數據。delete()方法用來刪除id對應數據。
添加對應的路由條目:
from django.contrib import admin from django.urls import path, re_path from demo import views urlpatterns = [ path('admin/', admin.site.urls), re_path('^publishes/$', views.PublishView.as_view()), re_path('^books/$', views.BookView.as_view()), re_path('^books/(\d+)/$', views.BookDetailView.as_view()), ]
注意,獲取單條數據的路由條目中有一個參數id。
2.實現get()獲取單條數據
class BookDetailView(APIView): def get(self, request, id): # 從數據庫中獲取id對應的數據 book = Book.objects.filter(pk=id).first() # 序列化 bs = BookModelSerializers(book) # 返回給瀏覽器 return Response(bs.data) def put(self, request, id): pass def delete(self, request, id): pass
3.實現put()修改數據
class BookDetailView(APIView): def get(self, request, id): # 從數據庫中獲取id對應的數據 book = Book.objects.filter(id=id).first() # 序列化 bs = BookModelSerializers(book) # 返回給瀏覽器 return Response(bs.data) def put(self, request, id): # 獲取pk=id的條目 book = Book.objects.filter(pk=id).first() # 獲取bs實例,並將修改的新值作為參數data bs = BookModelSerializers(book, data=request.data) # 判斷是否符合規則 if bs.is_valid(): # 更新,調用ModelSerializer中的update方法 bs.save() # 將更新成功的數據返回給瀏覽器 return Response(bs.data) else: # 將錯誤信息返回給瀏覽器 return HttpResponse(bs.errors) def delete(self, request, id): pass
4.實現delete()刪除數據
class BookDetailView(APIView): def get(self, request, id): # 從數據庫中獲取id對應的數據 book = Book.objects.filter(id=id).first() # 序列化 bs = BookModelSerializers(book) # 返回給瀏覽器 return Response(bs.data) def put(self, request, id): # 獲取pk=id的條目 book = Book.objects.filter(pk=id).first() # 獲取bs實例,並將修改的新值作為參數data bs = BookModelSerializers(book, data=request.data) # 判斷是否符合規則 if bs.is_valid(): # 更新,調用ModelSerializer中的update方法 bs.save() # 將更新成功的數據返回給瀏覽器 return Response(bs.data) else: # 將錯誤信息返回給瀏覽器 return HttpResponse(bs.errors) def delete(self, request, id): Book.objects.filter(pk=id).first().delete() return Response()
刪除最簡單,直接找到id對應的條目刪除即可。
十一、超鏈接字段
1.場景說明
我們使用127.0.0.1:8000/books/獲取到了所有書籍的信息:
[ { "id": 8, "title": "Python2標准庫3", "price": 99, "pub_date": "2012-11-20T13:03:33Z", "publish": 1, "authors": [ 1, 2 ] }, { "id": 9, "title": "Python2標准庫4", "price": 99, "pub_date": "2012-11-20T13:03:33Z", "publish": 1, "authors": [ 1, 2 ] }, { "id": 10, "title": "Python2標准庫5", "price": 99, "pub_date": "2012-11-20T13:03:33Z", "publish": 1, "authors": [ 1, 2 ] }, { "id": 11, "title": "Python2標准庫6", "price": 99, "pub_date": "2012-11-20T13:03:33Z", "publish": 1, "authors": [ 1, 2 ] }, { "id": 12, "title": "Python2標准庫7", "price": 99, "pub_date": "2012-11-20T13:03:33Z", "publish": 1, "authors": [ 1, 2 ] }, { "id": 13, "title": "Python2標准庫", "price": 99, "pub_date": "2012-11-20T13:03:33Z", "publish": 1, "authors": [ 1, 2 ] }, { "id": 14, "title": "Python3", "price": 99, "pub_date": "2020-01-20T13:03:04Z", "publish": 1, "authors": [ 3, 4 ] }, { "id": 15, "title": "JAVA", "price": 99, "pub_date": "2020-01-20T13:03:04Z", "publish": 1, "authors": [ 3, 4 ] }, { "id": 16, "title": "JAVA", "price": 99, "pub_date": "2020-01-20T13:03:04Z", "publish": 1, "authors": [ 3, 4 ] }, { "id": 17, "title": "JAVA", "price": 99, "pub_date": "2020-01-20T13:03:04Z", "publish": 1, "authors": [ 3, 4 ] }, { "id": 18, "title": "hello", "price": 99, "pub_date": "2020-01-20T13:03:04Z", "publish": 1, "authors": [ 3, 4 ] } ]
我們可以看到,每條書籍的記錄中"publish"字段都是以pk(主鍵ID)顯示的。
如果我們想將這個字段的值換成查詢相應publish的超鏈接,如下所示:
{ "id": 17, "title": "JAVA", "price": 99, "pub_date": "2020-01-20T13:03:04Z", "publish": "http://127.0.0.1:8000/publishes/1/", "authors": [ 3, 4 ] },
2.修改路由urls
要返回超鏈接格式的數據,url中需要使用有名分組。
from django.contrib import admin from django.urls import path, re_path from demo import views urlpatterns = [ path('admin/', admin.site.urls), re_path('^publishes/$', views.PublishView.as_view(), name="publish"), re_path('^publishes/(?P<pk>\d+)/$', views.PublishDetailView.as_view(), name="publishdetail"), re_path('^books/$', views.BookView.as_view(), name="book"), re_path('^books/(?P<pk>\d+)/$', views.BookDetailView.as_view(), name="bookdetail"), ]
url中要使用有名分組,分組名為pk。並且為相應url添加別名,name="publishdetail",因為在視圖函數中要使用反向生成url的功能。
3.視圖函數實現
由於我們要在book的全量查詢和單條記錄查詢中都要顯示publish的超鏈接,所以要對 BookModelSerializers類進行修改:
class BookModelSerializers(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" #生成url:http://127.0.0.1:8000/publishes/1/ publish = serializers.HyperlinkedIdentityField( # 指定urls中對應publish的url,即'^publishes/(?P<pk>\d+)/$' view_name="publishdetail", # 從Book表中獲取publish_id字段,填充到下面的lookup_url_kwarg參數指定的分組名中 lookup_field="publish_id", # 將上面的publish_id填充到urls中分組pk。生成url:http://127.0.0.1:8000/publishes/1/ lookup_url_kwarg="pk" )
然后,Book的全量展示或單條展示,要使用超鏈接的話,要在 BookModelSerializers 實例化的時候添加context參數:
class BookView(APIView): def get(self, request): book_list = Book.objects.all() bs = BookModelSerializers(book_list, many=True, context={'request': request}) return Response(bs.data) def post(self, request): bs = BookModelSerializers(data=request.data) if bs.is_valid(): bs.save() return Response(bs.data) else: return Response(bs.errors) class BookDetailView(APIView): def get(self, request, pk): # 從數據庫中獲取id對應的數據 book = Book.objects.filter(pk=pk).first() # 序列化 bs = BookModelSerializers(book, context={'request': request}) # 返回給瀏覽器 return Response(bs.data) def put(self, request, pk): # 獲取pk=id的條目 book = Book.objects.filter(pk=pk).first() # 獲取bs實例,並將修改的新值作為參數data bs = BookModelSerializers(book, data=request.data) # 判斷是否符合規則 if bs.is_valid(): # 更新,調用ModelSerializer中的update方法 bs.save() # 將更新成功的數據返回給瀏覽器 return Response(bs.data) else: # 將錯誤信息返回給瀏覽器 return HttpResponse(bs.errors) def delete(self, request, pk): Book.objects.filter(pk=pk).first().delete() return Response()
注意,由於urls中使用了有名分組,分組名為pk。所以BookView和BookDetailView中所有視圖函數接收參數的形參必須是"pk"。
最后查看books數據的結果:(http://127.0.0.1:8000/books/)
[ { "id": 8, "publish": "http://127.0.0.1:8000/publishes/1/", "title": "Python2標准庫3", "price": 99, "pub_date": "2012-11-20T13:03:33Z", "authors": [ 1, 2 ] }, { "id": 9, "publish": "http://127.0.0.1:8000/publishes/1/", "title": "Python2標准庫4", "price": 99, "pub_date": "2012-11-20T13:03:33Z", "authors": [ 1, 2 ] }, { "id": 10, "publish": "http://127.0.0.1:8000/publishes/1/", "title": "Python2標准庫5", "price": 99, "pub_date": "2012-11-20T13:03:33Z", "authors": [ 1, 2 ] } ]
查看單條book數據的結果:(http://127.0.0.1:8000/books/8/)
{ "id": 8, "publish": "http://127.0.0.1:8000/publishes/1/", "title": "Python2標准庫3", "price": 99, "pub_date": "2012-11-20T13:03:33Z", "authors": [ 1, 2 ] }
\ (•◡•) /
