创建新项目
- 新建DjangoREST项目
配置数据库驱动
App中__init__.py:
import pymysql pymysql.install_as_MySQLdb()
- settings.py
允许所有主机访问:
ALLOWED_HOSTS = ['*']
数据库设置
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'DjangoREST', 'USER': 'root', 'PASSWORD': '3825447403', 'HOST': 'localhost', 'PORT': 3306, } }
注册restframework
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'App.apps.AppConfig', ]
- 创建对应数据库
- 迁移数据库
REST中的类视图
官方中文文档:https://q1mi.github.io/Django-REST-framework-documentation/tutorial/3-class-based-views_zh/
可以使用基于类的视图编写我们的API视图,而不是基于函数的视图。我们将看到这是一个强大的模式,允许我们重用常用功能,提高代码复用率,并帮助我们保持代码DRY(整洁)。
如下图,APIView有如下的子类
GenericAPIView
这个是APIView的一个子类,增加了模型的获取操作。
get_queryset
获取查询结果集
def get_queryset(self):
"""
Get the list of items for this view.
This must be an iterable, and may be a queryset.
Defaults to using `self.queryset`.
This method should always be used rather than accessing `self.queryset`
directly, as `self.queryset` gets evaluated only once, and those results
are cached for all subsequent requests.
You may want to override this if you need to provide different
querysets depending on the incoming request.
(Eg. return a list of items that is specific to the user)
"""
assert self.queryset is not None, (
"'%s' should either include a `queryset` attribute, "
"or override the `get_queryset()` method."
% self.__class__.__name__
)
queryset = self.queryset
if isinstance(queryset, QuerySet):
# Ensure queryset is re-evaluated on each request.
queryset = queryset.all()
return queryset
get_object
获取单个对象,默认是查询主键pk:lookup_field =
`'pk'`
def get_object(self):
"""
Returns the object the view is displaying.
You may want to override this if you need to provide non-standard
queryset lookups. Eg if objects are referenced using multiple
keyword arguments in the url conf.
"""
queryset = self.filter_queryset(self.get_queryset())
# Perform the lookup filtering.
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
assert lookup_url_kwarg in self.kwargs, (
'Expected view %s to be called with a URL keyword argument '
'named "%s". Fix your URL conf, or set the `.lookup_field` '
'attribute on the view correctly.' %
(self.__class__.__name__, lookup_url_kwarg)
)
filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
obj = get_object_or_404(queryset, **filter_kwargs)
# May raise a permission denied
self.check_object_permissions(self.request, obj)
return obj
get_serializer
序列化实例
def get_serializer(self, *args, **kwargs):
"""
Return the serializer instance that should be used for validating and
deserializing input, and for serializing output.
"""
serializer_class = self.get_serializer_class()
kwargs.setdefault('context', self.get_serializer_context())
return serializer_class(*args, **kwargs)
get_serializer_class
获取序列化类
def get_serializer_class(self):
"""
Return the class to use for the serializer.
Defaults to using `self.serializer_class`.
You may want to override this if you need to provide different
serializations depending on the incoming request.
(Eg. admins get full serialization, others get basic serialization)
"""
assert self.serializer_class is not None, (
"'%s' should either include a `serializer_class` attribute, "
"or override the `get_serializer_class()` method."
% self.__class__.__name__
)
return self.serializer_class
get_serializer_context
获取上下文内容
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
return {
'request': self.request,
'format': self.format_kwarg,
'view': self
}
filter_queryset
对queryset进行过滤
def filter_queryset(self, queryset):
"""
Given a queryset, filter it with whichever filter backend is in use.
You are unlikely to want to override this method, although you may need
to call it either from a list view, or from a custom `get_object`
method if you want to apply the configured filtering backend to the
default queryset.
"""
for backend in list(self.filter_backends):
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset
paginator
分页器
@property
def paginator(self):
"""
The paginator instance associated with the view, or `None`.
"""
if not hasattr(self, '_paginator'):
if self.pagination_class is None:
self._paginator = None
else:
self._paginator = self.pagination_class()
return self._paginator
paginate_queryset
对结果集进行分页
def paginate_queryset(self, queryset):
"""
Return a single page of results, or `None` if pagination is disabled.
"""
if self.paginator is None:
return None
return self.paginator.paginate_queryset(queryset, self.request, view=self)
get_paginated_response
获取分页后的结果
def get_paginated_response(self, data):
"""
Return a paginated style `Response` object for the given output data.
"""
assert self.paginator is not None
return self.paginator.get_paginated_response(data)
这些方法就是封装的模型的获取操作,也就是对数据的CURD
补充
增删改查都要基于查询,为什么增也要基于查询?
因为当我们添加一条数据时,如果说要保证这条数据之前没有被创建过,需要先查询一次有没有创建,如果已经创建了就不允许再次创建,不能仅仅在前端做限制,还需要在后端添加数据时做查询检查。
比如买电影票:
按道理已经被预约的位置是不能再次被预定,并且页面已经禁止选中已经被预约的座位。
但是如果遇到程序员同行,对前端按钮状态修改。
可以看到,之前的位置已经可以选中
这样就能进行下单,如果不在后端创建订单时进行检查这个座位是否已经被预约,那么就会出现问题,所以创建数据时从业务逻辑严格的情况下是需要进行查询的。
可以看到如果下单,后端是做了防备的。
CreateAPIView
class CreateAPIView(mixins.CreateModelMixin,
GenericAPIView):
"""
Concrete view for creating a model instance.
"""
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
继承自mixins.CreateModelMixin
和GenericAPIView
mixins.CreateModelMixin
是mixins包中的类
class CreateModelMixin:
"""
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 {}
ListAPIView
列表的类视图
class ListAPIView(mixins.ListModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs) # 这个list方法是ListModelMixin中的
继承自mixins.ListModelMixin
和 GenericAPIView
mixins.ListModelMixin
class ListModelMixin:
"""
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)
serializer = self.get_serializer(queryset, many=True) # 获取序列化
return Response(serializer.data) # 返回json数据
RetrieveAPIView
查询单个数据的类视图
class RetrieveAPIView(mixins.RetrieveModelMixin,
GenericAPIView):
"""
Concrete view for retrieving a model instance.
"""
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
继承自mixins.RetrieveModelMixin
和GenericAPIView
mixins.RetrieveModelMixin
class RetrieveModelMixin:
"""
Retrieve a model instance.
"""
def retrieve(self, request, *args, **kwargs):
instance = self.get_object() # 获取单个对象
serializer = self.get_serializer(instance) # 序列化
return Response(serializer.data)
实现了get单个数据,并且序列化
DestroyAPIView
删除数据的类视图
class DestroyAPIView(mixins.DestroyModelMixin,
GenericAPIView):
"""
Concrete view for deleting a model instance.
"""
def delete(self, request, *args, **kwargs): # 实现了delete方法
return self.destroy(request, *args, **kwargs)
继承自mixins.DestroyModelMixin
和GenericAPIView
DestroyModelMixin
class DestroyModelMixin:
"""
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()
UpdateAPIView
更新数据的类视图
class UpdateAPIView(mixins.UpdateModelMixin,
GenericAPIView):
"""
Concrete view for updating a model instance.
"""
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def patch(self, request, *args, **kwargs):
return self.partial_update(request, *args, **kwargs)
继承自mixins.UpdateModelMixin
和 GenericAPIView
实现了PUT,PATCH请求。
mixins.UpdateModelMixin
class UpdateModelMixin:
"""
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): # 对应put方法
serializer.save()
def partial_update(self, request, *args, **kwargs): # 差量跟新,对应patch方法
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
ListCreateAPIView
获取列表数据,创建数据的类视图
class ListCreateAPIView(mixins.ListModelMixin,
mixins.CreateModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset or creating a model instance.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
继承自mixins.ListModelMixin
、mixins.CreateModelMixin
、GenericAPIView
实现了get,post方法.
RetrieveUpdateAPIView
获取单个对象,并且更新单个数据的类视图
class RetrieveUpdateAPIView(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
GenericAPIView):
"""
Concrete view for retrieving, updating a model instance.
"""
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def patch(self, request, *args, **kwargs):
return self.partial_update(request, *args, **kwargs)
继承自mixins.RetrieveModelMixin
、mixins.UpdateModelMixin
、GenericAPIView
实现了get,put,patch
RetrieveDestroyAPIView
获取单个数据,删除单个数据
class RetrieveDestroyAPIView(mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
GenericAPIView):
"""
Concrete view for retrieving or deleting a model instance.
"""
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
继承自mixins.RetrieveModelMixin
、mixins.DestroyModelMixin
、GenericAPIView
RetrieveUpdateDestroyAPIView
获取单个数据,更新单个数据,删除单个数据的类视图
class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
GenericAPIView):
"""
Concrete view for retrieving, updating or deleting a model instance.
"""
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def patch(self, request, *args, **kwargs):
return self.partial_update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
继承自:
- mixins.RetrieveModelMixin
- mixins.UpdateModelMixin
- mixins.DestroyModelMixin
- GenericAPIView
实现了get,put,delete
实践运用类视图
新建models
class Game(models.Model):
g_name = models.CharField(max_length=32)
g_price = models.FloatField(default=0)
然后迁移数据库
进行序列化
新建serializers.py
from rest_framework import serializers
from App.models import Game
class GameSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Game
fields = ['url','g_name','g_price']
views
views.py:
from django.shortcuts import render
from rest_framework.generics import ListCreateAPIView
from App.models import Game
from App.serializers import GameSerializer
# 创建数据视图
class GamesView(ListCreateAPIView):
# 实例化serializers.py中的序列化类
serializer_class = GameSerializer
queryset = Game.objects.all()
# 查询单个数据视图
class GameView(RetrieveUpdateDestroyAPIView):
serializer_class = GameSerializer
queryset = Game.objects.all()
urls
新建子urls.py
from django.urls import re_path
from App import views
urlpatterns = [
re_path(r'games/$', views.GamesView.as_view()),
re_path(r'games/(?P<pk>\d+)', views.GameView.as_view(), name='game-detail'),
]
因为使用serializers.HyperlinkedModelSerializer
这个序列化器,当创建完之后会自动查询刚刚创建的数据,所以需要第二条url:name='game-detail'
,否则会报没有这个detail的错误。
在总urls中注册
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('app/', include('App.urls')),
]
页面测试
可以使用post创建数据,然后使用get获取刚刚创建的数据
可以使用put修改已经创建的数据,也可以使用delete删除
从测试可以看到CRUD功能已经可以实现了,但是这样测试只能方便开发人员测试,并不适用于开发环境。
postman测试
get
查询已有数据
post
创建新数据
补充
差量更新patch和整体更新put
- put需要提供所有参数才能修改,如果model中该字段设置了默认值default,这个字段也可以不提供,如果说即没有设置默认值,也没有提供参数并且使用put,那么就会提示this field is required。
- 而差量更新只需要提供需要修改的那部分参数
总结
如果现在需要写增只需要使用create视图,查只需要使用retrieve视图,如果需要查询所有视图,使用list视图。
此处评论已关闭