11 Book系列連表操作
阿新 • • 發佈:2022-04-15
views.py
from django.shortcuts import render
# Create your views here.
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
from app01.models import Book
# from app01.ser import BookSerializers
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
# class TestView(APIView):
# def get(self,request):
# 1/0
# return Response({'msg':'個人中心'})
#
# class BookViewSet(ModelViewSet):
# authentication_classes = [BasicAuthentication,]
# queryset = Book.objects.all()
# serializer_class = BookSerializers
# @action(methods=['get'], detail=False)
# def login(self, request):
# Book.objects.update_or_create()
# return Response({'msg':'登陸成功'})
# @action(methods=['put'], detail=True)
# def get_new_5(self, request,pk):
# return Response({'msg':'獲取5條資料成功'})
from rest_framework.permissions import AllowAny,IsAuthenticated,IsAdminUser,IsAuthenticatedOrReadOnly
from app01.response import APIResponse
from app01 import models
from app01 import ser as serializers
class PublishAPIView(APIView):
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
publish_obj = models.Publish.objects.filter(pk=pk).first()
if not publish_obj:
return APIResponse(1, 'pk error', http_status=400)
publish_data = serializers.PublishModelSerializer(publish_obj).data
return APIResponse(results=publish_data)
publish_query = models.Publish.objects.all()
return APIResponse(0, 'ok', data=serializers.PublishModelSerializer(publish_query, many=True).data)
class BookAPIView(APIView):
# 單查、群查
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
book_obj = models.Book.objects.filter(pk=pk, is_delete=False).first()
if not book_obj:
return APIResponse(1, 'pk error', http_status=400)
book_data = serializers.BookModelSerializer(book_obj).data
print(book_data)
return APIResponse(data=book_data)
book_query = models.Book.objects.filter(is_delete=False).all()
return APIResponse(0, 'ok', data=serializers.BookModelSerializer(book_query, many=True).data)
# 單刪、群刪
def delete(self, request, *args, **kwargs):
"""
單刪:前臺資料為pk,介面為 /books/(pk)/
群刪:前臺資料為pks,介面為 /books/
"""
pk = kwargs.get('pk')
# 將單刪群刪邏輯整合
if pk: # /books/(pk)/的介面就不考慮群刪,就固定為單刪
pks = [pk]
else:
pks = request.data.get('pks')
# 前臺資料有誤(主要是群刪沒有提供pks)
if not pks:
return APIResponse(1, 'delete error', http_status=400)
# 只要有操作受影響行,就是刪除成功,反之失敗
rows = models.Book.objects.filter(is_delete=False, pk__in=pks).update(is_delete=True)
if rows:
return APIResponse(0, 'delete ok')
return APIResponse(1, 'delete failed')
# 單增、群增
def post(self, request, *args, **kwargs):
"""
單增:前臺提交字典,介面 /books/
群增:前臺提交列表套字典,介面 /books/
"""
request_data = request.data
if isinstance(request_data, dict): # 單增
book_ser = serializers.BookModelSerializer(data=request_data)
if book_ser.is_valid():
book_obj = book_ser.save()
return APIResponse(data=serializers.BookModelSerializer(book_obj).data)
return APIResponse(1, msg=book_ser.errors)
elif isinstance(request_data, list) and len(request_data) != 0 : # 群增
book_ser = serializers.BookModelSerializer(data=request_data, many=True)
book_ser.is_valid(raise_exception=True)
book_obj_list = book_ser.save()
return APIResponse(data=serializers.BookModelSerializer(book_obj_list, many=True).data)
else:
return APIResponse(1, 'data error', http_status=400)
# 單整體改、群整體改
def put(self, request, *args, **kwargs):
"""
單整體改:前臺提交字典,介面 /books/(pk)/
群整體改:前臺提交列表套字典,介面 /books/,注每一個字典都可以通過pk
"""
pk = kwargs.get('pk')
request_data = request.data
if pk: # 單改
try:
book_obj = models.Book.objects.get(pk=pk)
except:
return APIResponse(1, 'pk error')
# 修改和新增,都需要通過資料,資料依舊給data,修改與新增不同點,instance要被賦值為被修改物件
book_ser = serializers.BookModelSerializer(instance=book_obj, data=request_data)
book_ser.is_valid(raise_exception=True)
book_obj = book_ser.save()
return APIResponse(data=serializers.BookModelSerializer(book_obj).data)
else: # 群改
if not isinstance(request_data, list) or len(request_data) == 0:
return APIResponse(1, 'data error', http_status=400)
# [{pk:1,...}, {pk:3,...}, {pk:100,...}] => [obj1, obj3, obj100] + [{...}, {...}, {...}]
# 要考慮pk對應的物件是否被刪,以及pk沒有對應的物件
# 假設pk3被刪,pk100沒有 => [obj1] + [{...}]
# 注:一定不要在迴圈體中對迴圈物件進行增刪(影響物件長度)的操作
obj_list = []
data_list = []
for dic in request_data:
# request_data可能是list,單內部不一定是dict
try:
pk = dic.pop('pk')
try:
obj = models.Book.objects.get(pk=pk, is_delete=False)
obj_list.append(obj)
data_list.append(dic)
except:
pass
except:
return APIResponse(1, 'data error', http_status=400)
book_ser = serializers.BookModelSerializer(instance=obj_list, data=data_list, many=True)
book_ser.is_valid(raise_exception=True)
book_obj_list = book_ser.save()
return APIResponse(data=serializers.BookModelSerializer(book_obj_list, many=True).data)
# 單區域性改、群區域性改
def patch(self, request, *args, **kwargs):
"""
單整體改:前臺提交字典,介面 /books/(pk)/
群整體改:前臺提交列表套字典,介面 /books/,注每一個字典都可以通過pk
"""
pk = kwargs.get('pk')
request_data = request.data
if pk:
try:
book_obj = models.Book.objects.get(pk=pk)
except:
return APIResponse(1, 'pk error')
# 區域性修改就是在整體修改基礎上設定partial=True,將所有參與反序列化欄位設定為required=False
book_ser = serializers.BookModelSerializer(instance=book_obj, data=request_data, partial=True)
book_ser.is_valid(raise_exception=True)
book_obj = book_ser.save()
return APIResponse(data=serializers.BookModelSerializer(book_obj).data)
else: # 群改
if not isinstance(request_data, list) or len(request_data) == 0:
return APIResponse(1, 'data error', http_status=400)
# [{pk:1,...}, {pk:3,...}, {pk:100,...}] => [obj1, obj3, obj100] + [{...}, {...}, {...}]
# 要考慮pk對應的物件是否被刪,以及pk沒有對應的物件
# 假設pk3被刪,pk100沒有 => [obj1] + [{...}]
# 注:一定不要在迴圈體中對迴圈物件進行增刪(影響物件長度)的操作
obj_list = []
data_list = []
for dic in request_data:
# request_data可能是list,單內部不一定是dict
try:
pk = dic.pop('pk')
try:
obj = models.Book.objects.get(pk=pk, is_delete=False)
obj_list.append(obj)
data_list.append(dic)
except:
pass
except:
return APIResponse(1, 'data error', http_status=400)
book_ser = serializers.BookModelSerializer(instance=obj_list, data=data_list, many=True, partial=True)
book_ser.is_valid(raise_exception=True)
book_obj_list = book_ser.save()
return APIResponse(data=serializers.BookModelSerializer(book_obj_list, many=True).data)
class AuthorAPIView(APIView):
def get(self,request,*args,**kwargs):
authors=models.Author.objects.all()
author_ser=serializers.AuthorModelSerializer(authors,many=True)
return APIResponse(data=author_ser.data)
def put(self,reuqest,*args,**kwargs):
pass
def post(self,request,*args,**kwargs):
author_ser=serializers.AuthorModelSerializer(data=request.data)
author_ser.is_valid(raise_exception=True)
author_ser.save()
return APIResponse()
def delete(self,request,*args,**kwargs):
pass
ser.py
from rest_framework import serializers
from app01 import models
class BookListSerializer(serializers.ListSerializer):
# 1、create方法父級ListSerializer已經提供了
# def create(self, validated_data):
# # 通過self.child來訪問繫結的ModelSerializer
# print(self.child)
# raise Exception('我不提供')
# 2、父級ListSerializer沒有通過update方法的實現體,需要自己重寫
def update(self, instance, validated_data):
# print(instance)
# print(validated_data)
return [
self.child.update(instance[i], attrs) for i, attrs in enumerate(validated_data)
]
class BookModelSerializer(serializers.ModelSerializer):
# 通過BookModelSerializer.Meta.list_serializer_class來訪問繫結的ListSerializer
class Meta:
# 關聯ListSerializer完成群增群改
list_serializer_class = BookListSerializer
model = models.Book
# fields = ('name', 'price', 'publish', 'authors')
# fields = ('name', 'price', 'publish_name', 'author_list')
# 瞭解
# fields = '__all__'
# exclude = ('id', )
# depth = 1
# 序列化與反序列化整合
fields = ('name', 'price', 'publish_name', 'author_list', 'publish', 'authors')
extra_kwargs = {
'publish': {
'write_only': True
},
'authors': {
'write_only': True
}
}
# 前提:如果只有查需求的介面,自定義深度還可以用子序列化方式完成
class PublishModelSerializer(serializers.ModelSerializer):
# 子序列化都是提供給外來鍵(正向方向)完成深度查詢的,外來鍵資料是唯一:many=False;不唯一:many=True
# 注:只能參與序列化,且反序列化不能寫(反序列化外來鍵欄位會拋異常)
books = BookModelSerializer(many=True)
class Meta:
model = models.Publish
fields = ('name', 'address', 'books')
class AuthorModelSerializer(serializers.ModelSerializer):
class Meta:
model=models.Author
fields=('name','sex','mobile','mobile_in')
extra_kwargs={
'mobile':{
'read_only': True
},
}
mobile_in=serializers.CharField(write_only=True)
# def validate_mobile_in(self, data):
# print(data)
# return data
def create(self, validated_data):
print(validated_data)
mobile=validated_data.pop('mobile_in')
author=models.Author.objects.create(**validated_data)
authordetail=models.AuthorDetail.objects.create(mobile=mobile,author=author)
return author
models.py
from django.db import models
# 一、基表
# Model類的內部配置Meta類要設定abstract=True,這樣的Model類就是用來作為基表
# 多表:Book,Publish,Author,AuthorDetail
class BaseModel(models.Model):
is_delete = models.BooleanField(default=False)
create_time = models.DateTimeField(auto_now_add=True)
class Meta:
# 基表必須設定abstract,基表就是給普通Model類繼承使用的,設定了abstract就不會完成資料庫遷移完成建表
abstract = True
class Book(BaseModel):
name = models.CharField(max_length=16)
price = models.DecimalField(max_digits=5, decimal_places=2)
publish = models.ForeignKey(to='Publish', related_name='books', db_constraint=False, on_delete=models.DO_NOTHING)
# 重點:多對多外來鍵實際在關係表中,ORM預設關係表中兩個外來鍵都是級聯
# ManyToManyField欄位不提供設定on_delete,如果想設定關係表級聯,只能手動定義關係表
authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)
# 自定義連表深度,不需要反序列化,因為自定義插拔屬性不參與反序列化
@property
def publish_name(self):
return self.publish.name
@property
def author_list(self):
temp_author_list = []
for author in self.authors.all():
temp_author_list.append({
'name': author.name,
'sex': author.get_sex_display(),
'mobile': author.detail.mobile
})
return temp_author_list
class Publish(BaseModel):
name = models.CharField(max_length=16)
address = models.CharField(max_length=64)
class Author(BaseModel):
name = models.CharField(max_length=16)
sex = models.IntegerField(choices=[(0, '男'),(1, '女')], default=0)
class AuthorDetail(BaseModel):
mobile = models.CharField(max_length=11)
# 有作者可以沒有詳情,刪除作者,詳情一定會被級聯刪除
# 外來鍵欄位為正向查詢欄位,related_name是反向查詢欄位
author = models.OneToOneField(to='Author', related_name='detail', db_constraint=False, on_delete=models.CASCADE)
# 二、表斷關聯
# 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
# 三、ORM外來鍵設計
# 1、一對多:外來鍵放在多的一方
# 2、多對多:外來鍵放在常用的一方
# 3、一對一:外來鍵放在不常用的一方
# 4、外來鍵欄位為正向查詢欄位,related_name是反向查詢欄位
# from django.contrib.auth.models import AbstractUser, User
# class MyUser(AbstractUser):
# pass
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
urls.py
path(r'publishes/', views.PublishAPIView.as_view()),
re_path(r'^publishes/(?P<pk>\d+)/$', views.PublishAPIView.as_view()),
path(r'books/', views.BookAPIView.as_view()),
re_path(r'^books/(?P<pk>\d+)/$', views.BookAPIView.as_view()),