Day 24 DRF view set
One, DRF-ModelSerializer many-to-many data creation
models.py
class Book(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
author = models.ManyToManyField(to='Author', null=True) # 多对多关系
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE) # 外键关系
class Publish(models.Model):
name = models.CharField(max_length=32)
address = models.CharField(max_length=64)
class Author(models.Model):
name = models.CharField(max_length=32)
salary = models.IntegerField()
phone = models.CharField(max_length=11)
serializer.py
class Author(serializers.ModelSerializer):
class Meta:
# 关联
model = models.Author
# 选取需要显示的字段
fields = '__all__'
class Publish(serializers.ModelSerializer):
class Meta:
model = models.Publish
fields = ['name', 'address']
class BookModel(serializers.ModelSerializer):
author1 = Author(read_only=True)
publish = Publish() # 外键这样能够读出来但是不能存
# 存
# publish1 = Publish(read_only=True)
class Meta:
model = models.Book # 表模型 与之关联
fields = '__all__'
But it won’t work
No read_only = True
class BookModel(serializers.ModelSerializer):
author = Author()
publish = Publish()
This will not work, because the two fields correspond to table objects,
When get, because the original author corresponds to the relationship , it has become a table object (overwritten). Publish is the same, so the data cannot be found.
By default, when post is to be stored in the complete object , which is a dictionary
This problem is due to -many relationship is very special, he is a middle of the table of the original corresponding table relationships into table object can not find the corresponding data fields, so error, here we have reserved the original table relationships (author fields)
If we want to cross-table queries need to redefine a field name (author1)
, and set read_only
properties so that drf by ModelSerializer
looking for fields corresponding to the content, if not set will with their (author)
conflict, which is separate read and write , in charge of their own write_only
, Then you can read and write normally!
Two, view base class (2)
Django REST framwork provides the main role of views
- Control the execution of the serializer (check, save, convert data)
- Control database query
The biggest difference between the two is GenericAPIView继承APIView类的,在拥有APIView所有特性外,还提供了链接Model类与Serializer类的接口
1. APIView
It is the base class of all views provided by REST framework, inheriting native Django View
From the previous process, we know that he inherits the as_view method of the parent class, executes his own dispath, and then returns the REST framework Response
object. Of course, many things have happened!
APIView still uses conventional class view definition methods to implement get(), post() or other request methods.
2.GenericAPIView
Inherited from
APIVIew
, it mainly adds methods for operating serializers and database queries. Its role is to provide method support for the execution of the following Mixin extension classes. Usually, it can be used with one or more Mixin extension classes.
2.1 Support defined attributes
- List view and detail view are common
- queryset list view query set
- serializer_class The serializer used by the view
- List view usage
- pagination_class paging control class
- filter_bankends filter control backend
- Details page view usage
- lookup_field The condition field used when querying a single database object, the default is "pk"
- lookup_url_kwarg The name of the parameter keyword in the URL when querying a single data, the default is the same as look_field
2.2 Methods provided
- List view and detail view
get_queryset()
The query set used by the return view is the basis for the list view and the detail view to obtain data. The queryset attribute is returned by default, which can be overriddenget_serializer_class()
Return the serializer object, which is used by other views or extended classes. If we want to get the serializer object in the view, we can call this method directlyget_serializer()
Return the serializer object, which is used by other views or extended classes. If we want to get the serializer object in the view, we can call this method directly.get_object()
Return a model object required by the detail view, the lookup_field parameter is used by default to filter the queryset
Let's try GenericAPIView
serializer.py Same as above
views.py
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
class Book(GenericAPIView):
# 因为是queryset对象 可以自调用 加不加all()不重要,源码中默认加了 all()
queryset = models.Book.objects
serializer_class = serializer.BookModel
def get(self, request, *args, **kwargs):
books = self.get_queryset()
ser = self.get_serializer(books, many=True)
return Response(ser.data)
def post(self, request, *args, **kwargs):
ser = self.get_serializer(data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
Let's send a get request and get the data we need
Let's study the source code
Operation of checking, modifying and deleting a single record
urls.py
url(r'^bookinfo/(?P<pk>\d+)', views.BookInfo.as_view()),
views.py
The overall logic is similar to our previous APIView, with a slight change in method
class BookInfo(GenericAPIView):
queryset = models.Book.objects
serializer_class = serializer.BookModel
def get(self,request,*args, **kwargs):
book = self.get_object()
ser = self.get_serializer(book)
return Response(ser.data)
def patch(self,request,*args, **kwargs):
book = self.get_object()
ser = self.get_serializer(book, data = request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response('保存失败!')
def delete(self,request,*args, **kwargs):
book = self.get_object().delete()
return Response('删除成功!')
Only test the modification method this time
Source code analysis
Three, view extension class (5)
Provides several back-end views (deleted, modified, and checked data resources) to achieve the processing flow. If the views that need to be written belong to these five types, the views can reuse the code by inheriting the corresponding extension classes, reducing the code written by themselves the amount.
These five extension classes need to be matchedGenericAPIViewThe parent class, because the implementation of the five extended classes need to call the GenericAPIView providedSerializerversusDatabase queryMethods.
1. ListModelMixin
The list view extension class provides list(request, *args, **kwargs)
methods to quickly implement the list view and return a 200 status code. The Mixin's list method performs datafilterwithPagination
Source code:
Copyclass ListModelMixin(object):
"""
List a queryset.
"""
def list(self, request, *args, **kwargs):
# 过滤
queryset = self.filter_queryset(self.get_queryset())
# 分页
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
# 序列化 自己覆盖GenericAPIView的queryset和serializer
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
Give a chestnut
class Book(ListModelMixin, CreateModelMixin, GenericAPIView):
queryset = models.Book.objects
serializer_class = serializer.BookModel
def get(self, request, *args, **kwargs):
# 调用继承的ListModelMixin的list方法并返回
return self.list(request, *args, **kwargs)
2.CreateModelMixin
Create a view extension class, provide a create(request, *args, **kwargs)
method to quickly create a resource view, and successfully return a 201 status code.
If the serializer fails to verify the data sent by the front end, it returns a 400 error.
Source code:
Copyclass CreateModelMixin(object):
"""
Create a model instance.
"""
def create(self, request, *args, **kwargs):
# 获取序列化器
serializer = self.get_serializer(data=request.data)
# 验证
serializer.is_valid(raise_exception=True)
# 保存
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(self, serializer):
serializer.save()
def get_success_headers(self, data):
try:
return {
'Location': str(data[api_settings.URL_FIELD_NAME])}
except (TypeError, KeyError):
return {
}
3. RetrieveModelMixin
The detail view extension class provides retrieve(request, *args, **kwargs)
methods to quickly return an existing data object.
If it exists, return 200, otherwise return 404.
Source code:
Copyclass RetrieveModelMixin(object):
"""
Retrieve a model instance.
"""
def retrieve(self, request, *args, **kwargs):
# 获取对象,会检查对象的权限
instance = self.get_object()
# 序列化
serializer = self.get_serializer(instance)
return Response(serializer.data)
For example:
Copyclass BookDetailView(RetrieveModelMixin, GenericAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
def get(self, request, pk):
return self.retrieve(request)
4.UpdateModelMixin
The update view extension class provides update(request, *args, **kwargs)
methods to quickly update an existing data object.
At the same time, it also provides partial_update(request, *args, **kwargs)
methods to implement partial updates.
Successfully returns 200. When the serializer fails to verify the data, it returns 400 error.
Source code:
Copyclass UpdateModelMixin(object):
"""
Update a model instance.
"""
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance.
instance._prefetched_objects_cache = {
}
return Response(serializer.data)
def perform_update(self, serializer):
serializer.save()
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
5.DestroyModelMixin
Delete the view extension class and provide destroy(request, *args, **kwargs)
methods to quickly delete an existing data object.
Return 204 if successful, and return 404 if it does not exist
Source code:
Copyclass DestroyModelMixin(object):
"""
Destroy a model instance.
"""
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
self.perform_destroy(instance)
return Response(status=status.HTTP_204_NO_CONTENT)
def perform_destroy(self, instance):
instance.delete()
But this still has limitations, we still have to write two view classes, configure two routes
class Book(ListModelMixin, CreateModelMixin, GenericAPIView):
queryset = models.Book.objects
serializer_class = serializer.BookModel
def get(self, request, *args, **kwargs):
# 调用继承的ListModelMixin的list方法并返回
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class BookInfo(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView):
queryset = models.Book.objects
serializer_class = serializer.BookModel
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def patch(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
Is there a better way?
some!
Four, sub-category view (9)
1.CreateAPIView
Provide post method
Inherited from: GenericAPIView, CreateModelMixin
2.ListAPIView
Provide get method
Inherited from: GenericAPIView, ListModelMixin
3.RetrieveAPIView
Provide get method
Inherited from: GenericAPIView, RetrieveModelMixin
4.DestoryAPIView
Provide delete method
Inherited from: GenericAPIView, DestoryModelMixin
5.UpdateAPIView
Provide put and patch methods
Inherited from: GenericAPIView, UpdateModelMixin
6.RetrieveUpdateAPIView
Provide get, put, patch methods
Inherited from: GenericAPIView, RetrieveModelMixin, UpdateModelMixin
7.RetrieveUpdateDestoryAPIView
Provide get, put, patch, delete methods
Inherited from: GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestoryModelMixin
8. ListCreateAPIView: inherited
Get all, post method is added
Inherited from: ListModelMixin, CreateModelMixin, GenericAPIVie
9.RetrieveDestroyAPIView
There is a get method to get one, the delete method to delete
Inherited from: RetrieveModelMixin, DestroyModelMixin, GenericAPIView
Instructions
class Book(ListCreateAPIView, RetrieveDestroyAPIView, ):
# 根据多继承python3的广度优先,他会从左到右依次匹配相关的方法,进而执行
queryset = models.Book.objects
serializer_class = serializer.BookModel
One thing to note is that there is no conflict between single-record query and multi-record query, because they use different interfaces!
So is there a way to not even write an excuse?