django_restfreamwork 2安裝配置和序列化元件
DRF框架準備
django restframework 框架
mysql
新建庫
mysql> create database restframework charset utf8mb4;
Query OK, 1 row affected (0.00 sec)
配置xadmin
安裝
pip3 install https://codeload.github.com/sshwsfc/xadmin/zip/django2 -i https://pypi.tuna.tsinghua.edu.cn/simple
註冊xadmin
settings.py
INSTALLED_APPS = [ ... 'xadmin', 'crispy_forms', 'reversion', ... ] # 修改使用中文介面 LANGUAGE_CODE = 'zh-Hans' #LANGUAGE_CODE = 'en-us' # 修改時區 TIME_ZONE = 'Asia/Shanghai' #TIME_ZONE = 'UTC'
xadmin資料庫遷移
python3 manage.py makemigrations
python3 manage.py migrate
新增xadmin路由
新增總路由
# from django.contrib import admin # from django.urls import path,include # # from django.urls import re_path # from django.conf import settings # from django.views.static import serve import xadmin xadmin.autodiscover() # version模組自動註冊需要版本控制的 Model from xadmin.plugins import xversion xversion.register_models() #用xadmin,就不用admin要註釋, urlpatterns = [ # #path('admin/', admin.site.urls), # re_path(r'media/(?P<path>.*)', serve, {"document_root": settings.MEDIA_ROOT}), # path('', include("home.urls")), path(r'xadmin/', xadmin.site.urls), ]
建立xadmin超級使用者
$ python3 manage.py createsuperuser
使用者名稱 (leave blank to use 'wangjunxiang'): root
電子郵件地址: [email protected]
Password: 123123
Password (again): 123123
密碼長度太短。密碼必須包含至少 8 個字元。
這個密碼太常見了。
密碼只包含數字。
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.
配置xadminx.py
新建apps/自定義專案app名/adminx.py
- 注意:配置檔案一定叫 adminx.py
import xadmin
from xadmin import views
class BaseSetting(object):
"""xadmin的基本配置"""
enable_themes = True # 開啟主題切換功能
use_bootswatch = True
xadmin.site.register(views.BaseAdminView, BaseSetting)
class GlobalSettings(object):
"""xadmin的全域性配置"""
site_title = "luffy" # 設定站點標題
site_footer = "luffy" # 設定站點的頁尾
menu_style = "accordion" # 設定選單摺疊
xadmin.site.register(views.CommAdminView, GlobalSettings)
# 匯入表
from .models import 表1,表2,表3
class 表1_ModelAdmin(object):
list_display=["表字段1","表字段2","表字段3"]
xadmin.site.register(表1, 表1_ModelAdmin)
class 表2_ModelAdmin(object):
list_display=["表字段1",]
xadmin.site.register(表2, 表2_ModelAdmin)
class 表3_ModelAdmin(object):
list_display=["表字段1",]
xadmin.site.register(表3, 表3_ModelAdmin)
設定訪問白名單
settings.py中設定為 '*'
ALLOWED_HOSTS = ['*']
資料庫錄入資料
訪問 127.0.0.1:8000/xadmin
INSERT INTO `book` VALUES (1, '無限恐怖', 1, '2020-08-20', 1);
INSERT INTO `book` VALUES (2, '盤龍', 2, '2020-08-20', 2);
INSERT INTO `book` VALUES (3, '射鵰英雄傳', 3, '2020-08-20', 3);
INSERT INTO `book` VALUES (4, '星辰變與無限空間', 3, '2020-08-20', 3);
INSERT INTO `book` VALUES (5, 'asdadsasdasd', 3, '2020-08-20', 2);
INSERT INTO `book` VALUES (6, 'django', 1, '2020-08-20', 1);
BEGIN;
INSERT INTO `book_author` VALUES (1, 1, 1);
INSERT INTO `book_author` VALUES (2, 2, 3);
INSERT INTO `book_author` VALUES (3, 3, 2);
INSERT INTO `book_author` VALUES (4, 4, 1);
INSERT INTO `book_author` VALUES (5, 4, 3);
INSERT INTO `book_author` VALUES (6, 5, 1);
INSERT INTO `book_author` VALUES (7, 5, 2);
INSERT INTO `book_author` VALUES (8, 5, 3);
INSERT INTO `book_author` VALUES (9, 6, 4);
COMMIT;
BEGIN;
INSERT INTO `publisher` VALUES (1, '集英社');
INSERT INTO `publisher` VALUES (2, '華中出版社');
INSERT INTO `publisher` VALUES (3, '清華大學出版社');
COMMIT;
配置DRF框架
安裝DRF
pip3 install djangorestframework==3.11.0
註冊DRF
settings.py
INSTALLED_APPS = [
....
....
#匯入DRF框架
'rest_framework',
]
DRF【序列化元件】
序列化:get()
前端發起請求,後端給前端返回資料
反序列化:post()
前端請求後端並帶著資料,後端進行欄位校驗後,更新資料,返回前端
通用的模型/總路由
總路由做路由分發
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', include("SerDemo.urls")),
]
models表結構
from django.db import models
# Create your models here.
__all__ = ["Book", "Publisher", "Author"]
class Book(models.Model):
title = models.CharField(max_length=32, verbose_name="圖書名稱")
CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
category = models.IntegerField(choices=CHOICES, verbose_name="圖書的類別")
pub_time = models.DateField(verbose_name="圖書的出版日期")
publisher = models.ForeignKey(to="Publisher", on_delete=None)
author = models.ManyToManyField(to="Author")
def __str__(self):
return self.title
class Meta:
db_table = "book"
verbose_name_plural = "圖書表"
class Publisher(models.Model):
title = models.CharField(max_length=32, verbose_name="出版社的名稱")
def __str__(self):
return self.title
class Meta:
db_table = "publisher"
verbose_name_plural = "出版社表"
class Author(models.Model):
name = models.CharField(max_length=32, verbose_name="作者的姓名")
def __str__(self):
return self.name
class Meta:
db_table = "author"
verbose_name_plural = "作者表"
遷移資料庫
python3 manage.py makemigrations
python3 manage.py migrate
繼承【Serializer】序列化器例項
1,檢視多條資料的介面
設定子路由介面
urls.py
from django.urls import path, include
from .views import BookView
urlpatterns = [
path('list', BookView.as_view()),
]
繼承【APIView】的檢視
views.py
from .models import Book, Publisher #引入表
from rest_framework.views import APIView #引入DRF框架
from rest_framework.response import Response #引入DRF返回
from .serializers import BookSerializer #引入序列化器
class BookView(APIView):
def get(self, request):
"""序列化例項"""
book_list = Book.objects.all()
ret = BookSerializer(book_list, many=True)
return Response(ret.data)
def post(self, request):
"""
反序列化例項
當序列化器需要儲存資料到資料庫,需要在序列化器中重寫 create方法
"""
print(request.data)
serializer = BookSerializer(data=request.data) #傳反序列化的資料
if serializer.is_valid(): #序列化器對前端傳來的資料校驗
serializer.save() #驗證通過,儲存到資料庫
return Response(serializer.validated_data)
else:
return Response(serializer.errors)
新建序列化器
新建 serializers.py檔案
from rest_framework import serializers
from .models import Book
class PublisherSerializer(serializers.Serializer):
"""序列化器巢狀"""
id = serializers.IntegerField()
title = serializers.CharField(max_length=32)
class AuthorSerializer(serializers.Serializer):
"""序列化器巢狀"""
id = serializers.IntegerField()
name = serializers.CharField(max_length=32)
class BookSerializer(serializers.Serializer):
"""
read_only=True 前端對後端發起請求,後端返回資料給前端用,這個欄位就定義為 read_only
write_only=True 前端提交欄位修改資料庫欄位的值用到,讓前端提交欄位為 w_欄位名,而不是資料庫原先的欄位,需要和前端說好
"""
id = serializers.IntegerField(required=False)
title = serializers.CharField(max_length=32)
CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
category = serializers.ChoiceField(choices=CHOICES, source="get_category_display", read_only=True)
w_category = serializers.ChoiceField(choices=CHOICES, write_only=True)
pub_time = serializers.DateField()
# 同理,凡是前端讀取的欄位都用read_only,凡是前端修改資料的都是write_only
publisher = PublisherSerializer(read_only=True)
publisher_id = serializers.IntegerField(write_only=True)
author = AuthorSerializer(many=True, read_only=True)
author_list = serializers.ListField(write_only=True)
def create(self, validated_data):
"""
當序列化器對欄位校驗成功後,儲存新的資料,必須要重寫create方法,views檢視中 serializer.save() 才能不報錯
validated_data:前端傳來的資料都在這個裡面
"""
"""
模擬前端傳送json資料
{
"title": "Alex的使用教程",
"w_category": 1,
"pub_time": "2018-10-09",
"publisher_id": 1,
"author_list": [1, 2]
}
"""
book = Book.objects.create(
title=validated_data["title"],
category=validated_data["w_category"],
pub_time=validated_data["pub_time"],
publisher_id=validated_data["publisher_id"],
)
book.author.add(*validated_data["author_list"]) #.add對author表新增多條記錄
return book
驗證
查詢所有資料【get請求】
當get請求 http://0.0.0.0:8000/books/list
檢視到所有的資料
新增一條資料【post請求】
當給 http://0.0.0.0:8000/books/list 傳送post請求的 json 資料,提交成功
json資料如下
{
"title": "寸芒",
"w_category": 1,
"pub_time": "2018-10-09",
"publisher_id": 2,
"author_list": [2, 3]
}
2,檢視單條資料的介面
設定子路由介面
urls.py 新增路由
檢視單條資料需要攜帶id過來
# from django.urls import path, include
#from .views import BookView
from .views import BookEditView
urlpatterns = [
# path('list', BookView.as_view()),
path('retrieve/<int:id>', BookEditView.as_view()),
]
繼承【APIView】的檢視
views.py 新增一個檢視類
from .models import Book
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import BookSerializer
class BookEditView(APIView):
def get(self, request, id):
"""檢視單條資料,前端需要傳id進來,get要接收id"""
book_obj = Book.objects.filter(id=id).first()
ret = BookSerializer(book_obj)
return Response(ret.data)
def put(self, request, id):
"""
當序列化器修改某一條資料,需要重新寫序列化器中 的update方法
"""
"""
前端put請求攜帶如下資料修改這條記錄
{
"title":"神鵰俠侶",
}
"""
book_obj = Book.objects.filter(id=id).first()
#第一個引數傳需要更新的物件,第二個引數傳需要更新的資料,第三個引數partial=True允許進行部分更新
serializer = BookSerializer(book_obj, data=request.data, partial=True)
if serializer.is_valid(): #對欄位進行驗證
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors)
def delete(self, request, id):
book_obj = Book.objects.filter(id=id).first()
book_obj.delete()
return Response("")
修改序列化器函式
serializers.py檔案
from rest_framework import serializers
from .models import Book
class PublisherSerializer(serializers.Serializer):
id = serializers.IntegerField()
title = serializers.CharField(max_length=32)
class AuthorSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField(max_length=32)
def my_validate(value):
"""
自定義鉤子方法
例如,傳的值不能含有「敏感資訊」這4個字串
"""
if "敏感資訊" in value.lower():
raise serializers.ValidationError("不能含有敏感資訊") #如果有,丟擲異常
else:
return value
class BookSerializer(serializers.Serializer):
"""
read_only=True 前端對後端發起請求,後端返回資料給前端用,這個欄位就定義為 read_only
write_only=True 前端提交欄位修改資料庫欄位的值用到,讓前端提交欄位為 w_欄位名,而不是資料庫原先的欄位,需要和前端說好
"""
id = serializers.IntegerField(required=False)
title = serializers.CharField(max_length=32, validators=[my_validate]) #validators=[] 列表中傳入自定義鉤子方法
CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
category = serializers.ChoiceField(choices=CHOICES, source="get_category_display", read_only=True)
w_category = serializers.ChoiceField(choices=CHOICES, write_only=True)
pub_time = serializers.DateField()
# 同理,凡是前端讀取的欄位都用read_only,凡是前端修改資料的都是write_only
publisher = PublisherSerializer(read_only=True)
publisher_id = serializers.IntegerField(write_only=True)
author = AuthorSerializer(many=True, read_only=True)
author_list = serializers.ListField(write_only=True)
def create(self, validated_data):
"""
當序列化器對欄位校驗成功後,儲存新的資料,必須要重寫create方法,views檢視中 serializer.save() 才能不報錯
validated_data:前端傳來的資料都在這個裡面
"""
"""
模擬前端傳送json資料
{
"title": "寸芒",
"w_category": 1,
"pub_time": "2011-01-09",
"publisher_id": 2,
"author_list": [2, 3]
}
"""
book = Book.objects.create(
title=validated_data["title"],
category=validated_data["w_category"],
pub_time=validated_data["pub_time"],
publisher_id=validated_data["publisher_id"],
)
book.author.add(*validated_data["author_list"]) #.add對author表新增多條記錄
return book
def update(self, instance, validated_data):
"""
instance 就是檢視傳過來的第一個引數:需要更新的模型物件,比如傳過來的book_obj,裡面有title,category欄位
"""
instance.title = validated_data.get("title", instance.title) #如果第一個引數title取不到會報錯,就給個第二個值不會報錯了
instance.category = validated_data.get("category", instance.category)
instance.pub_time = validated_data.get("pub_time", instance.pub_time)
instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id) #還能修改foreignkey
if validated_data.get("author_list"):
"""用來修改manytomany的欄位"""
# instance就是book_obj,author就是book表中manytomany的欄位
# ORM中manytomany新增是.add(),修改是.set()
instance.author.set(validated_data["author_list"])
instance.save()
return instance
def validate_title(self, value):
"""
區域性鉤子函式:對title欄位資料進行校驗
例如:title欄位中必須含有python
"""
if "python" not in value.lower():
#如果驗證失敗,則丟擲異常
raise serializers.ValidationError("標題必須含有python")
return value
def validate(self, attrs):
"""
全域性鉤子函式: 對多個欄位進行校驗
例如,當w_category是1(python),publisher是1的時候,驗證才通過
attrs 是字典 {"欄位名","值"}
"""
if attrs["w_category"] == 1 and attrs["publisher_id"] == 1:
return attrs
else:
raise serializers.ValidationError("分類以及標題不符合要求")
序列化器的驗證功能
區域性鉤子函式【權重低】
與自定義鉤子函式相比,先進行自定義鉤子函式的校驗,權重比較低
在序列化器中通過定義 def validate_想要驗證的欄位名(self, value)
的函式,校驗單個定義的欄位,來自定義驗證方法
class BookSerializer(serializers.Serializer):
.....
def create(self, validated_data):
.....
def update(self, instance, validated_data):
......
def validate_title(self, value):
"""
區域性鉤子函式:對title欄位資料進行校驗
例如:title欄位中必須含有python
"""
if "python" not in value.lower():
#如果驗證失敗,則丟擲異常
raise serializers.ValidationError("標題必須含有python")
return value
全域性鉤子函式
直接在序列化器中定義 def validate(self, attrs)
函式,用來校驗多個欄位
class BookSerializer(serializers.Serializer):
.....
def create(self, validated_data):
.....
def update(self, instance, validated_data):
.....
def validate_title(self, value):
.....
def validate(self, attrs):
"""
全域性鉤子函式: 對多個欄位進行校驗
例如,當w_category是1(python),publisher是1的時候,驗證才通過
attrs 是字典 {"欄位名","值"}
"""
if attrs["w_category"] == 1 and attrs["publisher_id"] == 1:
return attrs
else:
raise serializers.ValidationError("分類以及標題不符合要求")
自定義鉤子函式【權重高】
與區域性鉤子函式相比,先進行自定義鉤子函式的校驗,權重高
需要在類外面單獨定義個函式 def 自定義函式名(value)
,裡面寫驗證邏輯
某個欄位呼叫這個自定義鉤子函式,需要在引數後面加引數 alidators=[] 列表中傳入自定義鉤子方法
def my_validate(value):
"""
自定義鉤子方法
例如,傳的值不能含有「敏感資訊」這4個字串
"""
if "敏感資訊" in value.lower():
raise serializers.ValidationError("不能含有敏感資訊") #如果有,丟擲異常
else:
return value
class BookSerializer(serializers.Serializer):
.....
title = serializers.CharField(max_length=32, validators=[my_validate]) #validators=[] 列表中傳入自定義鉤子方法,代表對這個欄位進行校驗
.....
def create(self, validated_data):
.....
def update(self, instance, validated_data):
.....
def validate_title(self, value):
.....
def validate(self, attrs):
.....
測試
檢視單條資料【get請求】
請求 http://0.0.0.0:8000/books/retrieve/1 介面
修改單條資料【put請求】
請求 http://0.0.0.0:8000/books/retrieve/1 介面
驗證單條資料【put請求:序列化器中的區域性鉤子函式實現】
對 http://0.0.0.0:8000/books/retrieve/5 介面發起put請求,在鉤子函式中會定義內容中是否含有python如果沒有則丟擲異常
帶python的資料才會成功
繼承【ModelSerializer】序列化器例項
之前繼承 serializers.Serializer 需要寫很多欄位,很麻煩,為了解決繁瑣的程式碼,不用寫表的所有欄位都定義了,繼承 serializers.ModelSerializer 自動的解決
檢視多條資料的介面
設定子路有介面
from django.urls import path, include
from .views import BookView
urlpatterns = [
path('list', BookView.as_view()),
]
繼承【APIView】的檢視
from .models import Book
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import BookSerializer
class BookView(APIView):
def get(self, request):
book_list = Book.objects.all()
ret = BookSerializer(book_list, many=True)
return Response(ret.data)
def post(self, request):
""" 當序列化器需要儲存資料到資料庫,需要在序列化器中重寫 create方法 """
print(request.data)
serializer = BookSerializer(data=request.data) #傳反序列化的資料
if serializer.is_valid(): #序列化器對前端傳來的資料校驗
serializer.save() #驗證通過,儲存到資料庫
return Response(serializer.data)
else:
return Response(serializer.errors)
序列化器
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
"""繼承ModelSerializer,序列化器會與models進行結合"""
class Meta:
"""先對序列化器的model進行配置"""
model = Book
fields = "__all__" # 返回__all__所有欄位,或者指定欄位["id", "title", "pub_time"]
# depth = 1 #代表外來鍵巢狀層數為1層,有可能外來鍵關聯的表還會外來鍵關聯第三張表,這代表停到第一層就停止
extra_kwargs = {
"category": {"write_only": True}, # category 欄位不寫的話,會返回出去,這個欄位不想通過介面提供出去
"publisher": {"write_only": True}, # 就寫在這裡,這個欄位作為反序列化post請求過來修改欄位用
"author": {"write_only": True}
}
############ SerializerMethodField 取出關聯表想要的欄位,不想要的可以不要 ,下面的變數名會帶著值變成返回給前端 ##########
category_display = serializers.SerializerMethodField(read_only=True)
# 如果拿出想外來鍵對應表中的欄位,不是外來鍵id,下面需要繼續定義 def get_<publisher/欄位名> 函式,返回想要關聯表(外來鍵/manytomany)的欄位
publisher_info = serializers.SerializerMethodField(read_only=True)
# manytomany欄位對應表中的值,下面需要繼續定義 def get_<authors/欄位名> 函式
authors = serializers.SerializerMethodField(read_only=True)
def get_category_display(self,obj):
return obj.get_category_display() # orm方法 get_欄位_display ,獲取chiose中文 ((1,張三),(2,李四))
def get_publisher_info(self, obj):
"""
函式以 get_欄位命名
obj是序列化的每個book物件,因為Class meta 中定義了model=book
"""
publisher_obj = obj.publisher # 根據publisher外來鍵取 Publisher 表裡的值
return {
"id": publisher_obj.id,
"title": publisher_obj.title
}
def get_authors(self, obj):
"""
函式以 get_欄位命名
obj是序列化的每個book物件,因為Class meta 中定義了model=book
"""
authors_query_set = obj.author.all()
return [{"id": author_obj.id, "name": author_obj.name} for author_obj in authors_query_set]
測試
檢視所有資料【get請求】
當get請求 http://0.0.0.0:8000/books/list
新增一條資料【post請求】
對介面 http://0.0.0.0:8000/books/list post請求 攜帶資料
不需要重寫create方法,自動新增資料
新增如下如下資料
{
"title": "Alex的使用教程2",
"category": 1,
"pub_time": "2018-10-09",
"publisher": 1,
"author": [
1,
2
]
}
Serializer和ModelSerializer區別
Serializer 和 ModelSerializer的區別
https://www.cnblogs.com/xiugeng/p/11460855.html