序列化常見欄位
和models裡面的欄位是一一對應的,知識多出了兩個欄位
多出了倆 ListField DictField
欄位 | 欄位構造方式 |
---|---|
BooleanField | BooleanField() |
NullBooleanField | NullBooleanField() |
CharField | CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True) |
EmailField | EmailField(max_length=None, min_length=None, allow_blank=False) |
RegexField | RegexField(regex, max_length=None, min_length=None, allow_blank=False) |
SlugField | SlugField(maxlength=50, min_length=None, allow_blank=False) 正則欄位,驗證正則模式 [a-zA-Z0-9-]+ |
URLField | URLField(max_length=200, min_length=None, allow_blank=False) |
UUIDField | UUIDField(format=’hex_verbose’) format: 1) 'hex_verbose' 如"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex' 如 "5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a" |
IPAddressField | IPAddressField(protocol=’both’, unpack_ipv4=False, **options) |
IntegerField | IntegerField(max_value=None, min_value=None) |
FloatField | FloatField(max_value=None, min_value=None) |
DecimalField | DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位數 decimal_palces: 小數點位置 |
DateTimeField | DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None) |
DateField | DateField(format=api_settings.DATE_FORMAT, input_formats=None) |
TimeField | TimeField(format=api_settings.TIME_FORMAT, input_formats=None) |
DurationField | DurationField() |
ChoiceField | ChoiceField(choices) choices與Django的用法相同 |
MultipleChoiceField | MultipleChoiceField(choices) |
FileField | FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ImageField | ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ListField | ListField(child=, min_length=None, max_length=None) |
DictField | DictField(child=) |
序列化常見欄位引數
作用: 用來做反序列化校驗的。
通用欄位
required 表明該欄位在反序列化時必須輸入,預設為True
defatult 反序列化時使用的預設值
allow_null 表明該欄位是否允許傳入None,預設False
validators 驗證欄位的合法性
erorr_message 包含錯誤變化於錯誤資訊的字典
label 用於HTML展示API頁面時,顯示的欄位名稱
help_text 用於HTML展示API頁面時,顯示的欄位幫助提示資訊
--------- 非常重要 ----------
read_only 表明該欄位僅用於序列化輸出,預設False,一般是給前端資料的時候使用
write_one 表明該欄位僅用於反序列化輸入,預設False 一般用於往資料庫裡面存放資料
# CharField
max_length 最大長度
min_length 最小長度
allow_length 是否允許為空
trim_withspace 是否截斷空白字元
建立表
# models.py
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32, verbose_name="書籍名稱")
price = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="書籍價格")
publish_date = models.DateField(verbose_name="出版日期")
publish = models.ForeignKey(to='publish', on_delete=models.CASCADE, verbose_name="外來鍵,關聯出版社,設定級聯更新和級聯刪除")
# 資料庫中不會有這個欄位。
# 會生成第三張表 authors
authors = models.ManyToManyField(to="Authors")
class Meta:
db_table = "book"
class Publish(models.Model):
name = models.CharField(max_length=32, verbose_name='出版社名稱')
addr = models.CharField(max_length=32, verbose_name="出版社地址")
class Meta:
db_table = "publish"
class Authors(models.Model):
name = models.CharField(max_length=32, verbose_name='作者姓名')
age = models.IntegerField(verbose_name="作者的年齡")
class Meta:
db_table = "authors"
驗證合法性 validators 欄位自己的校驗 (很少用到)
會把欄位的值,傳到指定的函式里面去做校驗,如果驗證透過正常返回,如果驗證失敗觸發異常。 定義的函式形參名稱隨意。
# 如何使用
# 在類外面定義一個函式,函式放一個形參,名稱可以隨意
# 在對應的欄位中,validators=[ 建立的函式 ], 比如 name = serializers.CharField(validators=[check_field])
from rest_framework import serializers
from datetime import datetime
from rest_framework.exceptions import ValidationError
# 自己定義的函式,用於給validators校驗
def check_name(text):
if "sb" in text:
raise ValidationError("包含不合法的輸入 sb ")
else:
return text
class BookSerializer(serializers.Serializer):
name = serializers.CharField(max_length=8, validators=[check_name]) # 支援放入多個validators=[check_name, check_length...]
price = serializers.DecimalField(max_digits=5, decimal_places=2)
# 這裡的allow_null如果設定了,一般需要結合預設值一起去設定才會生效
publish_date = serializers.DateField(allow_null=True, default=datetime.now)
自定義錯誤資訊返回字典 error_message
# 自定義序列化類.py
name = serializers.CharField(max_length=8, min_length=2, error_messages={"max_length": "書名最多8位", "min_length": "書名最少2位"})
# 前端報錯資訊
{
"code": 100,
"msg": {
"name": [
"書名最少2位"
]
}
}
返回定製欄位
自定義返回的名稱 source
# book_name 表示代表顯示在前端的欄位, source=name 這個name就表示在模型層中建立的欄位
book_name = serializers.CharField(source="name")
# 可以跨表查詢
publish_name = serializers.CharField(source="publish.name")
publish_addr = serializers.CharField(source='publish.addr')
# 所有的欄位,都可以設定轉成CharField
# 可以 但是非特殊情況不建議 建議按照規範去操作
# publish_id = serializers.CharField(source="publish.id")
publish_id = serializers.IntegerField(source="publish.id")
ListField 和 DictField
- 單個結果用字典,即DictField
- 多個結果用列表,即ListField
# 後期想實現如下格式返回的資料
{name:書名,price:價格,publish:{name:出版社名,addr:地址},authors:[{},{}]}
# 方式一:在表模型中定義方法
在模型表中定義方法 需要模型表和序列化類一起配合使用
# 序列化類裡面的欄位
# 出版社物件詳細資訊
publish_detail = serializers.DictField()
# 作者詳細資訊
authors_detail = serializers.ListField()
# 模型層中定義
@property
def publish_detail(self):
# 這裡返回列表還是欄位,取決於序列化類的欄位定義的是 ListField 還是 DictField
return {"publish_name": self.publish.name, "publish_addr": self.publish.addr}
@property
def authors_detail(self):
items = []
# 因為這裡是 透過 manytomany建立的第三張表, authors 是一個物件,要拿全部資料要透過 all
for author in self.authors.all():
items.append(model_to_dict(author))
透過此方法返回自定義欄位,而不透過source實現
思考?這樣操作有什麼好處?
- 更加靈活的定製要返回的資料,隱藏一些敏感資訊,或者增加一些資訊
- 比如身份證號、銀行卡、電話號碼、地址等,隱藏部分敏感資訊。
# 自定義序列化類
book_name = serializers.CharField()
# 模型層
@property
def book_name(self):
return '可愛的作者:' + self.name
程式碼
# 檢視層
from django.shortcuts import render, HttpResponse
from django.views import View
from .serializers import BookSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book
from django.forms.models import model_to_dict
class BookView(APIView):
def get(self, request):
book_obj = Book.objects.all()
serializer = BookSerializer(instance=book_obj, many=True)
return Response({"code": 100, "msg": "查詢成功!", "results": serializer.data})
def post(self, request):
serializer = BookSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response({"code": 100, "msg": "新增成功!"})
else:
return Response({"code": 100, "msg": serializer.errors})
# 模型層
from django.db import models
from django.forms.models import model_to_dict
class Book(models.Model):
name = models.CharField(max_length=32, verbose_name="書籍名稱")
price = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="書籍價格")
publish_date = models.DateField(verbose_name="出版日期")
# 這裡是一個物件,所以可以直接透過 . 獲取資料
publish = models.ForeignKey(to='publish', on_delete=models.CASCADE, verbose_name="外來鍵,關聯出版社,設定級聯更新和級聯刪除")
# 資料庫中不會有這個欄位。
# 會生成第三張表 authors
authors = models.ManyToManyField(to="Authors")
class Meta:
db_table = "book"
# 如果要在序列化類使用欄位,那麼一定要保證模型層中有
# 所以這裡包裝成資料屬性,這樣序列化類可以直接使用,然後渲染給前端
@property
def publish_detail(self):
# 這裡返回列表還是欄位,取決於序列化類的欄位定義的是 ListField 還是 DictField
return {"publish_name": self.publish.name, "publish_addr": self.publish.addr}
@property
def authors_detail(self):
items = []
# 因為這裡是 透過 manytomany建立的第三張表, authors 是一個物件,要拿全部資料要透過 all
for author in self.authors.all():
items.append(model_to_dict(author))
return items
@property
def book_name(self):
return '經典書籍:' + self.name
class Publish(models.Model):
name = models.CharField(max_length=32, verbose_name='出版社名稱')
addr = models.CharField(max_length=32, verbose_name="出版社地址")
class Meta:
db_table = "publish"
def __str__(self):
return self.name
class Authors(models.Model):
name = models.CharField(max_length=32, verbose_name='作者姓名')
age = models.IntegerField(verbose_name="作者的年齡")
class Meta:
db_table = "authors"
# 序列化類
from rest_framework import serializers
from datetime import datetime
from rest_framework.exceptions import ValidationError
from .models import Book
def check_name(book_name):
if "sb" in book_name:
raise ValidationError("包含不合法的輸入 sb ")
else:
return book_name
class BookSerializer(serializers.Serializer):
# book_name = serializers.CharField(source="name", max_length=8, validators=[check_name])
# 嘗試透過 在模型層定義方法 返回 book_name 不適用 source
book_name = serializers.CharField()
price = serializers.DecimalField(max_digits=5, decimal_places=2, error_messages={"max_digits": "最少5為"})
# 這裡的allow_null如果設定了,一般需要結合預設值一起去設定才會生效
publish_date = serializers.DateField(allow_null=True, default=datetime.now)
# publish = serializers.CharField(source="publish.id") 這裡不需要指定publish.id 不然會報錯
# 如果這樣寫,會把publish的列印的樣子給前端,models可以配合__str__列印描述資訊,但是不建議這樣
# 因為是一個物件,我需要裡面更多的資料,而不是單單列印一個publish.name (models)設定的__str__
# publish = serializers.CharField()
publish = serializers.CharField()
# 出版社物件詳細資訊
publish_detail = serializers.DictField()
# 作者詳細資訊
# authors_detail = serializers.ListField()
# 可以跨表查詢
#
publish_id = serializers.CharField(source="publish.id")
# publish_id = serializers.IntegerField(source="publish.id")
publish_name = serializers.CharField(source="publish.name")
publish_addr = serializers.CharField(source='publish.addr')
def create(self, validated_data):
publish = validated_data.pop("publish")
book = Book.objects.create(**validated_data, publish_id=publish)
return book
透過 SerializerMethodField 去定製 只用到序列化類和模型表沒有關係
- 一定要配合一個方法 get_欄位名
- 其他的和上面的ListField/DictField一樣
# 嘗試透過 SerializerMethodField 返回book_name
book_name = serializers.SerializerMethodField()
# 出版社物件詳細資訊
publish_detail = serializers.SerializerMethodField()
# 作者詳細資訊
authors_detail = serializers.SerializerMethodField()
def get_publish_detail(self, obj):
# 這個obj 就是當前序列化的 book 物件
return model_to_dict(obj.publish)
def get_authors_detail(self, obj):
# 因為是第三張表 不要忘記 all 拿取所有
return [model_to_dict(author) for author in obj.authors.all()]
# 也可以自己去定製
def get_book_name(self, obj):
return "必讀經典書籍:" + obj.name
子序列化
- 定義一個類,繼承
serializers.Serializer
- 透過名稱指定即可:publish_detail = PublisuSerializer(source="publish")
- 如果是多條需要指定many=True
- 多條,一般是多對多的第三張表
- 單條,一般是一對多的第三張表
# 定義子序列化類,一般是有外來鍵關係的 多對多 一對多
class PublisuSerializer(serializers.Serializer):
# 這下面就寫要序列化給前端的欄位
id = serializers.IntegerField() # 出版社ID
name = serializers.CharField() # 出版社名稱
addr = serializers.CharField() # 出版社地址
class AuthorsSerializer(serializers.Serializer):
id = serializers.IntegerField() # 作者ID
name = serializers.CharField() # 作者姓名
small_age = serializers.IntegerField(source="age") # 作者年齡(重新命名為small_age)
class BookSerializer(serializers.Serializer):
# 子序列化 展示出版社詳細資訊
# 一樣可以透過source去指定名稱
# 拿著 PublisuSerializer 類裡面的格式去做序列化
publish_detail = PublisuSerializer(source="publish") # 出版社詳細資訊序列化器
# 子序列化,展示作者詳細資訊
authors_detail = AuthorsSerializer(many=True, source="authors") # 作者詳細資訊序列化器(多個)
反序列化儲存 即 儲存前端校驗透過的資料
read_only和write_only
- 後端傳遞給前端的資料,使用read_only
- 後端反序列化前端傳過來的資料,寫入到資料庫裡面,使用write_only
- read_only和write_only一起使用
- 如果是read_only的欄位,不填寫前端不會報錯必填。
- 反序列化的欄位,可以隨意命名,跟表欄位沒關係,但是後續儲存和修改要對應好才行。
# 注意:
1 read_only write_only 控制序列化類中某些欄位,只用來序列化或反序列化
2 重寫updata和create,儲存邏輯,我們自己寫
3 檢視類中 serializer = BookSerializer(instance=pk, data=request.data)
-後續在序列化類中的update中def update(self, instance, validated_data):
-instance就是當時給的
程式碼
# 序列化類.py
from rest_framework import serializers
from datetime import datetime
from rest_framework.exceptions import ValidationError
from .models import Book
from django.forms.models import model_to_dict
# 定義子序列化類,一般是有外來鍵關係的 多對多 一對多
class PublisuSerializer(serializers.Serializer):
# 這下面就寫要序列化給前端的欄位
id = serializers.IntegerField()
name = serializers.CharField()
addr = serializers.CharField()
class AuthorsSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField()
small_age = serializers.IntegerField(source="age")
class BookSerializer(serializers.Serializer):
book_name = serializers.CharField(source="name")
price = serializers.DecimalField(max_digits=5, decimal_places=2)
publish_date = serializers.DateField(allow_null=True, default=datetime.now)
# 子序列化 展示出版社詳細資訊
# 一樣可以透過source去指定名稱
publish_detail = PublisuSerializer(source="publish", read_only=True)
# 子序列化,展示作者詳細資訊
authors_detail = AuthorsSerializer(many=True, source="authors", read_only=True)
# 反序列化 儲存資料
# 這裡的publish 是models的一對多外來鍵 前端傳入數字 會自動根出版社的id做對應
publish = serializers.IntegerField(write_only=True)
# 多對多 是列表 所以要使用ListField
authors = serializers.ListField(write_only=True)
def create(self, validated_data):
# {'name': '天龍八部', 'price': Decimal('89.00'), 'publish_date': datetime.datetime(2024, 4, 12, 22, 18, 5, 785495), 'publish': 1, 'authors': [1]}
publish_id = validated_data.pop("publish")
authors = validated_data.pop("authors")
# 先獲取物件
book = Book.objects.create(**validated_data, publish_id=publish_id)
# 向中間表插入資料
book.authors.add(*authors)
return book
# 更新1 前端傳入的是 pk
def update(self, instance, validated_data):
publish_id = validated_data.pop("publish")
authors = validated_data.pop("authors")
# 下面的方法會報錯 因為uodate返回的並不是qs物件
# book = Book.objects.filter(pk=instance).update(**validated_data, publish_id=publish_id)
# book.authors.set(*authors) # AttributeError: 'int' object has no attribute 'authors'
book_qs = Book.objects.filter(pk=instance) # 找到這個qs物件
book_qs.update(**validated_data, publish_id=publish_id) # 使用qs去更新
obj = book_qs.first()
obj.authors.set(authors)
# 更新2 前端傳入的是物件
# def update(self, instance, validated_data):
# publish_id = validated_data.pop("publish")
# authors = validated_data.pop("authors")
# for attr, value in validated_data.items():
# setattr(instance, attr, value)
# instance.publish_id = publish_id
# instance.authors.set(authors)
# instance.save()
# return instance
# views.py
from django.shortcuts import render, HttpResponse, get_object_or_404
from django.views import View
from rest_framework import status
from .serializers import BookSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book
from django.forms.models import model_to_dict
class BookView(APIView):
def get(self, request):
book_obj = Book.objects.all()
serializer = BookSerializer(instance=book_obj, many=True)
return Response({"code": 100, "msg": "查詢成功!", "results": serializer.data})
# 新增一本書
def post(self, request):
serializer = BookSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response({"code": 100, "msg": "新增成功!"})
else:
return Response({"code": 100, "msg": serializer.errors})
class BookDetailView(APIView):
def get(self, request, pk):
obj = Book.objects.filter(pk=pk).first()
serializer = BookSerializer(instance=obj)
return Response({"code": 100, "msg": serializer.data})
def delete(self, request, pk):
instance = get_object_or_404(Book, pk=pk)
instance.delete()
return Response({"code": 100, "msg": "刪除成功!"}, status=status.HTTP_204_NO_CONTENT)
def put(self, request, pk):
# obj = Book.objects.filter(pk=pk).first() 如果要修改,因為這裡是多表關聯,所以可以不用傳入物件,傳入pk操作更方便
# 傳入的instance是什麼,到了update中,就是什麼。
serializer = BookSerializer(instance=pk, data=request.data)
if serializer.is_valid():
serializer.save()
return Response({"code": 100, "msg": "修改成功!"})
else:
return Response({"code": 100, "msg": serializer.errors})
ModelSerializer的使用 重要!
- 新增和更新,不需要再寫了,不過欄位要一一對應,不能修改,預設模型表定義的什麼欄位,序列化類裡面就要什麼欄位,(看write_only)
- 不過如果是新增或者修改,instance不能傳入pk了,只能傳入物件。
- 區域性鉤子和全域性鉤子跟之前一樣(注意層級),不要寫到Meta裡面去了,需要和Meta同級。
# views.py
def put(self, request, pk):
obj = Book.objects.filter(pk=pk).first() # 如果使用serializers.ModelSerializer 這裡只能傳入一個物件
serializer = BookSerializer(instance=obj, data=request.data)
if serializer.is_valid():
serializer.save()
return Response({"code": 100, "msg": "修改成功!"})
else:
return Response({"code": 100, "msg": serializer.errors})
# models.py
from django.db import models
from django.forms.models import model_to_dict
class Book(models.Model):
name = models.CharField(max_length=32, verbose_name="書籍名稱")
price = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="書籍價格")
publish_date = models.DateField(verbose_name="出版日期")
# 這裡是一個物件,所以可以直接透過 . 獲取資料
publish = models.ForeignKey(to='publish', on_delete=models.CASCADE, verbose_name="外來鍵,關聯出版社,設定級聯更新和級聯刪除")
# 資料庫中不會有這個欄位。
# 會生成第三張表 authors
authors = models.ManyToManyField(to="Authors")
class Meta:
db_table = "book"
class Publish(models.Model):
name = models.CharField(max_length=32, verbose_name='出版社名稱')
addr = models.CharField(max_length=32, verbose_name="出版社地址")
class Meta:
db_table = "publish"
def __str__(self):
return self.name
class Authors(models.Model):
name = models.CharField(max_length=32, verbose_name='作者姓名')
age = models.IntegerField(verbose_name="作者的年齡")
class Meta:
db_table = "authors"
# 自定義序列化類.py
from rest_framework import serializers
from datetime import datetime
from rest_framework.exceptions import ValidationError
from .models import Book
from django.forms.models import model_to_dict
# 定義子序列化類,一般是有外來鍵關係的 多對多 一對多
class PublisuSerializer(serializers.Serializer):
# 這下面就寫要序列化給前端的欄位
id = serializers.IntegerField()
name = serializers.CharField()
addr = serializers.CharField()
class AuthorsSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField()
small_age = serializers.IntegerField(source="age")
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book # 要關聯的表
fields = "__all__" # 要關聯哪些欄位
# 如果要關聯指定的欄位 加入到一個列表裡面 列表裡面放進去欄位
# 這個欄位需要用引號包裹起來
# fields = ["name", "publish" ...] #
# 做控制,有的使用序列化,有的使用反序列化
extra_kwargs = {
"publish": {"write_only": True},
"name": {"max_length": 8}, # 控制引數
"authors": {"write_only": True}
}
# 如果有自己額外的子序列化欄位,需要class Meta 同級新增, 不要寫到 Meta 內部了!!!
publish_detail = PublisuSerializer(read_only=True, source="publish")
authors_detail = AuthorsSerializer(read_only=True, source="authors", many=True)