drf多表斷表操作
阿新 • • 發佈:2020-07-13
目錄
settings.py
INSTALLED_APPS = [ # ... 'rest_framework', ] DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'dg_proj', 'USER': 'root', 'PASSWORD': '123', } } # 連線mysql資料庫 """ 任何__init__檔案 import pymysql pymysql.install_as_MySQLdb() """ # 國際化處理 LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai' USE_I18N = True USE_L10N = True USE_TZ = False # 靜態檔案的環境配置 MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
models.py
""" Book表:name、price、img、authors、publish、is_delete、create_time Pbulish表:name、address、is_delete、create_time Author表:name、age、is_delete、create_time AutherDetail表:mobile、author、is_delete、create_time BaseModel基表 is_delete、create_tiime 上面四個表的建立繼承基表。可以繼承兩個欄位 """ """ 外來鍵處理: 反向查詢名字:related_name 表關係:db_constraint + on_delete db_constraint=False => 斷開表關係 on_delete=models.CASCADE 級聯 on_delete=models.SET_NULL, null=True 設定為空 on_delete=models.SET_DEFAULT, default=0 設定成預設值0 on_delete=models.DO_NOTHING 不處理 注:多對多關係不需要明確on_delete """ """ 1、外來鍵位置: 一對多 - 外來鍵放多的一方 一對一 - 從邏輯正反向考慮,如作者表與作者詳情表,作者刪除級聯刪除詳情, 詳情刪除作者依舊存在,所以建議外來鍵在詳情表中 多對多 - 外來鍵在關係表中 2、ORM正向方向連表查詢: 正向:通過外來鍵欄位 eg: author_detial_obj.author 反向:通過related_name的值 eg:author_obj.detail 注:依賴程式碼見下方 3、連表操作關係: 1)作者刪除,詳情級聯 - on_delete=models.CASCADE 2)作者刪除,詳情置空 - null=True, on_delete=models.SET_NULL 3)作者刪除,詳情重置 - default=0, on_delete=models.SET_DEFAULT 4)作者刪除,詳情不動 - on_delete=models.DO_NOTHING 注:拿作者與作者詳情表舉例 4、外來鍵關聯欄位的引數 - 如何實現 斷關聯、目前表間操作關係、方向查詢欄位 i)作者詳情表中的 author = models.OneToOneField( to='Author', related_name='detail', db_constraint=False, on_delete=models.CASCADE ) ii)圖書表中的 publish = models.ForeignKey( to='Publish', related_name='books', db_constraint=False, on_delete=models.DO_NOTHING, ) authors = models.ManyToManyField( to='Author' related_name='books', db_constraint=False, ) 注:ManyToManyField不能設定on_delete,OneToOneField、 ForeignKey必須設定on_delete(django1.x系統預設級聯, 但是django2.x必須手動明確) """ from django.db import models # 圖書管理系統:Book、Author、AuthorDetail、Publish """ Book表: name、price、img、authors、publish、is_delete、create_time Publish表: name、address、is_delete、create_time Author表: name、age、is_delete、create_time AuthorDetail表: mobile, author、is_delete、create_time """ # 1) 基表(把表相同的欄位單獨創建出來形成基表,讓其他表直接繼承即可。) class BaseModel(models.Model): is_delete = models.BooleanField(default=False) create_time = models.DateTimeField(auto_now_add=True) # 設定 abstract =True 來宣告基表,作為基表的Model不能在資料庫中形成對應的表 class Meta: abstract = True class Book(BaseModel): """name、price、img、authors、publish、is_delete、create_time""" name = models.CharField(max_length=64) price = models.DecimalField(max_digits=5, decimal_places=2) img = models.ImageField(upload_to='img', default='img/default.jpg') publish = models.ForeignKey( to='Publish', db_constraint=False, # 斷關聯 related_name='books', # 反向查詢欄位:publish_obj.books 就能訪問所有出版的書 on_delete=models.DO_NOTHING, # 設定連表操作關係 ) authors = models.ManyToManyField( to='Author', db_constraint=False, related_name='books' ) # 序列化插拔式屬性 - 完成自定義欄位名完成連表查詢 @property def publish_name(self): return self.publish.name # 可插拔設計 @property def author_list(self): return self.authors.values('name', 'age', 'detail__mobile').all() class Meta: db_table = 'book' verbose_name = '書籍' verbose_name_plural = verbose_name def __str__(self): return self.name class Publish(BaseModel): """name、address、is_delete、create_time""" name = models.CharField(max_length=64) address = models.CharField(max_length=64) class Meta: db_table = 'publish' verbose_name = '出版社' verbose_name_plural = verbose_name def __str__(self): return self.name class Author(BaseModel): """name、age、is_delete、create_time""" name = models.CharField(max_length=64) age = models.IntegerField() class Meta: db_table = 'author' verbose_name = '作者' verbose_name_plural = verbose_name def __str__(self): return self.name class AuthorDetail(BaseModel): """mobile, author、is_delete、create_time""" mobile = models.CharField(max_length=11) author = models.OneToOneField( to='Author', db_constraint=False, related_name='detail', on_delete=models.CASCADE, ) class Meta: db_table = 'author_detail' verbose_name = '作者詳情' verbose_name_plural = verbose_name def __str__(self): return '%s的詳情' % self.author.name
serialiaers.py
from rest_framework.serializers import ModelSerializer, SerializerMethodField from rest_framework.exceptions import ValidationError from . import models # 可以單獨作為Publish介面的序列化類,也可以作為Book序列化外來鍵publish輔助的序列化元件 class PublishModelSerializer(ModelSerializer): class Meta: model = models.Publish fields = ('name', 'address') class BookModelSerializer1(ModelSerializer): # 瞭解: 該方式設定的序列化欄位,必須在fields中宣告 # publish_address = SerializerMethodField() # def get_publish_address(self, obj): # return obj.publish.address # 自定義連表深度 - 子序列化方式 - 該方式不能參與反序列化,使用在序列化反序列化共存時,不能書寫 publish = PublishModelSerializer() ## 獲取關於publish所有屬性 class Meta: # 序列化類關聯的model類 model = models.Book # 參與序列化的欄位 fields = ('name', 'price', 'img', 'author_list', 'publish') # 瞭解知識點 # 所有欄位 # fields = '__all__' # 與fields不共存,exclude排除哪些欄位 # exclude = ('id', 'is_delete', 'create_time') # 自動連表深度 # depth = 1 ## 反序列化 class BookModelDeserializer2(ModelSerializer): class Meta: model = models.Book fields = ('name', 'price', 'publish', 'authors') # extra_kwargs 用來完成反序列化欄位的 系統校驗規則 extra_kwargs = { 'name': { 'required': True, 'min_length': 1, 'error_messages': { 'required': '必填項', 'min_length': '太短', } } } # 區域性鉤子 def validate_name(self, value): # 書名不能包含 g 字元 if 'g' in value.lower(): raise ValidationError('該g書不能出版') return value # 全域性鉤子 def validate(self, attrs): publish = attrs.get('publish') name = attrs.get('name') if models.Book.objects.filter(name=name, publish=publish): raise ValidationError({'book': '該書已存在'}) return attrs # ModelSerializer類已經幫我們實現了 create 與 update 方法 """ (1)、fields中設定所有序列化與反序列化欄位 (2)、extra_kwargs劃分只序列化欄位 wirte_only:只反序列化 read_only:只序列化 自定義欄位預設只序列化(read_only) (3)、設定反序列化所需的系統、區域性鉤子、全域性鉤子等校驗規則 """ ## 序列化與反序列化合並 class V2BookModelSerializer(ModelSerializer): class Meta: model = models.Book fields = ('name', 'price', 'img', 'author_list', 'publish_name', 'publish', 'authors') extra_kwargs = { 'name': { 'required': True, 'min_length': 1, 'error_messages': { 'required': '必填項', 'min_length': '太短', } }, 'publish': { 'write_only': True }, 'authors': { 'write_only': True }, 'img': { 'read_only': True, }, 'author_list': { 'read_only': True, }, 'publish_name': { 'read_only': True, } } def validate_name(self, value): # 書名不能包含 g 字元 if 'g' in value.lower(): raise ValidationError('該g書不能出版') return value def validate(self, attrs): publish = attrs.get('publish') name = attrs.get('name') if models.Book.objects.filter(name=name, publish=publish): raise ValidationError({'book': '該書已存在'}) return attrs
views.py
from rest_framework.response import Response
from rest_framework.views import APIView
from . import models, serializers
class Book(APIView):
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
try:
book_obj = models.Book.objects.get(pk=pk, is_delete=False)
book_data = serializers.BookModelSerializer1(book_obj).data
except:
return Response({
'status': 1,
'msg': '書籍不存在'
})
else:
book_query = models.Book.objects.filter(is_delete=False).all()
book_data = serializers.BookModelSerializer1(book_query, many=True).data
return Response({
'status': 0,
'msg': 'ok',
'results': book_data
})
def post(self, request, *args, **kwargs):
request_data = request.data
book_ser = serializers.BookModelDeserializer2(data=request_data)
# raise_exception=True:當校驗失敗,馬上終止當前檢視方法,拋異常返回給前臺
book_ser.is_valid(raise_exception=True)
book_obj = book_ser.save()
return Response({
'status': 0,
'msg': 'ok',
'results': serializers.BookModelSerializer1(book_obj).data
})
class V2Book(APIView):
# 單查:有pk
# 群查:無pk
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
try:
book_obj = models.Book.objects.get(pk=pk, is_delete=False)
book_data = serializers.V2BookModelSerializer(book_obj).data
except:
return Response({
'status': 1,
'msg': '書籍不存在'
})
else:
book_query = models.Book.objects.filter(is_delete=False).all()
book_data = serializers.V2BookModelSerializer(book_query, many=True).data
return Response({
'status': 0,
'msg': 'ok',
'results': book_data
})
# 單增:傳的資料是與model對應的字典
# 群增:傳的資料是 裝多個 model對應字典 的列表
def post(self, request, *args, **kwargs):
request_data = request.data
if isinstance(request_data, dict):
many = False
elif isinstance(request_data, list):
many = True
else:
return Response({
'status': 1,
'msg': '資料有誤',
})
book_ser = serializers.V2BookModelSerializer(data=request_data, many=many)
# 當校驗失敗,馬上終止當前檢視方法,拋異常返回給前臺
book_ser.is_valid(raise_exception=True)
book_result = book_ser.save()
return Response({
'status': 0,
'msg': 'ok',
'results': serializers.V2BookModelSerializer(book_result, many=many).data
})
# 單刪:有pk
# 群刪:有pks | {"pks": [1, 2, 3]}
def delete(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
pks = [pk]
else:
pks = request.data.get('pks')
if models.Book.objects.filter(pk__in=pks, is_delete=False).update(is_delete=True):
return Response({
'status': 0,
'msg': '刪除成功',
})
return Response({
'status': 1,
'msg': '刪除失敗',
})
urls.py
urlpatterns = [
url(r'^books/$', views.Book.as_view()),
url(r'^books/(?P<pk>.*)/$', views.Book.as_view()),
url(r'^v2/books/$', views.V2Book.as_view()),
url(r'^v2/books/(?P<pk>.*)/$', views.V2Book.as_view()),
]
案例
"""
models.py
"""
from django.db import models
class BaseModel(models.Model):
is_delete=models.BooleanField(default=False)
# auto_now_add=True 只要記錄建立,不需要手動插入時間,自動把當前時間插入
create_time=models.DateTimeField(auto_now_add=True)
# auto_now=True,只要更新,就會把當前時間插入
last_update_time=models.DateTimeField(auto_now=True)
# import datetime
# create_time=models.DateTimeField(default=datetime.datetime.now)
class Meta:
# 單個欄位,有索引,有唯一
# 多個欄位,有聯合索引,聯合唯一
abstract=True # 抽象表,不再資料庫建立出表
class Book(BaseModel):
id=models.AutoField(primary_key=True)
# verbose_name admin中顯示中文
name=models.CharField(max_length=32,verbose_name='書名',help_text='這裡填書名')
price=models.DecimalField(max_digits=5,decimal_places=2)
# 一對多的關係一旦確立,關聯欄位寫在多的一方
#to_field 預設不寫,關聯到Publish主鍵
#db_constraint=False 邏輯上的關聯,實質上沒有外來鍵練習,增刪不會受外來鍵影響,但是orm查詢不影響
publish=models.ForeignKey(to='Publish',on_delete=models.DO_NOTHING,db_constraint=False)
# 多對多,跟作者,關聯欄位寫在 查詢次數多的一方
# 什麼時候用自動,什麼時候用手動?第三張表只有關聯欄位,用自動 第三張表有擴充套件欄位,需要手動寫
# 不能寫on_delete
authors=models.ManyToManyField(to='Author',db_constraint=False)
class Meta:
verbose_name_plural='圖書' # admin中表名的顯示
def __str__(self):
return self.name
@property
def publish_name(self):
return self.publish.name
# def author_list(self):
def author_list(self):
# return self.authors.all().values()
author_list=self.authors.all()
# ll=[]
# for author in author_list:
# ll.append({'name':author.name,'sex':author.get_sex_display()})
# return ll
return [ {'name':author.name,'sex':author.get_sex_display()} for author in author_list]
class Publish(BaseModel):
name = models.CharField(max_length=32)
addr=models.CharField(max_length=32)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = '出版社'
class Author(BaseModel):
name=models.CharField(max_length=32)
sex=models.IntegerField(choices=((1,'男'),(2,'女')))
# 一對一關係,寫在查詢頻率高的一方
#OneToOneField本質就是ForeignKey+unique,自己手寫也可以
authordetail=models.OneToOneField(to='AuthorDetail',db_constraint=False,on_delete=models.CASCADE)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = '作者'
class AuthorDetail(BaseModel):
mobile=models.CharField(max_length=11)
class Meta:
verbose_name_plural = '作者詳情'
def __str__(self):
return self.mobile
# 二、表斷關聯
# 1、表之間沒有外來鍵關聯,但是有外來鍵邏輯關聯(有充當外來鍵的欄位)
# 2、斷關聯後不會影響資料庫查詢效率,但是會極大提高資料庫增刪改效率(不影響增刪改查操作)
# 3、斷關聯一定要通過邏輯保證表之間資料的安全,不要出現髒資料,程式碼控制
# 4、斷關聯
# 5、級聯關係
# 作者沒了,詳情也沒:on_delete=models.CASCADE
# 出版社沒了,書還是那個出版社出版:on_delete=models.DO_NOTHING
# 部門沒了,員工沒有部門(空不能):null=True, on_delete=models.SET_NULL
# 部門沒了,員工進入預設部門(預設值):default=0, on_delete=models.SET_DEFAULT
"""
admin.py
"""
from django.contrib import admin
# Register your models here.
from app02 import models
admin.site.register(models.Book)
admin.site.register(models.Publish)
admin.site.register(models.Author)
admin.site.register(models.AuthorDetail)
"""
serializers.py
"""
from rest_framework import serializers
from app02 import models
## 重寫ListSerializer中的update方法(原始碼中群更新update方法沒寫)
class BookListSerializer(serializers.ListSerializer):
def update(self, instance, validated_data):
return [
self.child.update(instance[i], attr) for i,attr in enumerate(validated_data)
]
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
## 關聯群跟新update方法
list_serializer_class = BookListSerializer
model = models.Book
fields = ['name', 'price', 'authors', 'publish', 'author_list', 'publish_name',]
extra_kwargs = {
'authors': {'write_only':True,},
'publish': {'write_only':True,},
'author_list': {'read_only':True,},
'publish_name': {'read_only':True,},
}
"""
views.py
"""
from django.shortcuts import render
from rest_framework.generics import GenericAPIView
from app02.serializers import BookModelSerializer
from app02 import models
from utils.response import RespUtil
from rest_framework.pagination import PageNumberPagination
# Create your views here.
from rest_framework.serializers import ListSerializer
## 分頁
class SelfPagination(PageNumberPagination):
page_size = 3
page_size_query_param = "size"
max_page_size = 5
class BookGenericAPIView(GenericAPIView):
book_qs = models.Book.objects.all().filter(is_delete=False)
queryset = book_qs
serializer_class = BookModelSerializer
def get(self, request, *args, **kwargs):
page_obj = SelfPagination()
book_list = page_obj.paginate_queryset(self.book_qs, request, self)
# next_url = page_obj.get_next_link()
# pr_url = page_obj.get_previous_link()
pk = kwargs.get('pk')
if pk:
ser = self.serializer_class(self.get_object())
else:
ser = self.serializer_class(book_list, many=True)
return RespUtil(data=ser.data)
def post(self, request, *args, **kwargs):
if isinstance(request.data, dict):
ser = self.serializer_class(data=request.data)
else:
ser = self.serializer_class(data=request.data, many=True)
ser.is_valid(raise_exception=True)
ser.save()
return RespUtil(data=ser.data)
def put(self, request, *args, **kwargs):
if kwargs.get('pk'):
book = models.Book.objects.filter(pk=kwargs.get('pk')).first()
ser = self.serializer_class(instance=book, data=request.data, partial=True)
else:
obj = []
data = []
for item in request.data:
pk = item.pop('id')
book = models.Book.objects.filter(pk=pk).first()
obj.append(book)
data.append(item)
## 不重寫update方法迴圈呼叫update方法一個個跟新也行
# for i, v in enumerate(data):
# ser = self.serializer_class(instance=obj[i], data=v)
# ser.is_valid(raise_exception=True)
# ser.save()
ser = self.serializer_class(instance=obj, data=data, many=True)
ser.is_valid(raise_exception=True)
ser.save()
return RespUtil(data=ser.data)
def delete(self, request, *args, **kwargs):
pk = kwargs.get('pk')
pks = []
if pk:
pks.append(pk)
else:
pks = request.data.get('pks')
res = models.Book.objects.filter(id__in=pks, is_delete=False).update(is_delete=True)
if res:
return RespUtil(msg='OK')
return RespUtil(msg='無要刪除的資料')
"""
urls.py
"""
from django.conf.urls import url
from app02 import views
urlpatterns = [
url(r'^v2/books/$', views.BookGenericAPIView.as_view()),
url(r'^v2/books/(?P<pk>\d+)/', views.BookGenericAPIView.as_view()),
]