1. 程式人生 > 實用技巧 >django_restfreamwork 2安裝配置和序列化元件

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