Django REST framework+Vue 打造生鲜超市(四)
五、商品列表页
5.1.django的view实现商品列表页
(1)goods/view_base.py
在goods文件夹下面新建view_base.py,为了区分django和django rest framework的view
利用Django的view实现返回json数据
# goods/view_base.py
from django.views.generic import View
from goods.models import Goods
class GoodsListView(View):
def get(self,request):
#通过django的view实现商品列表页
json_list = []
#获取所有商品
goods = Goods.objects.all()
for good in goods:
json_dict = {}
#获取商品的每个字段,键值对形式
json_dict['name'] = good.name
json_dict['category'] = good.category.name
json_dict['market_price'] = good.market_price
json_list.append(json_dict)
from django.http import HttpResponse
import json
#返回json,一定要指定类型content_type='application/json'
return HttpResponse(json.dumps(json_list),content_type='application/json')
(2)MxShop/urls.py
from goods.view_base import GoodsListView
urlpatterns = [
#商品列表页
path('goods/',GoodsListView.as_view(),name='goods-list')
]
访问http://127.0.0.1:8000/goods/ 可以获取商品列表信息的json数据
5.2.django的serializer序列化model
(1)model_to_dict
当字段比较多时,一个字段一个字段的提取很麻烦,可以用model_to_dict,将model整个转化为dict
# goods/view_base.py
from django.views.generic import View
from goods.models import Goods
class GoodsListView(View):
def get(self,request):
#通过django的view实现商品列表页
json_list = []
#获取所有商品
goods = Goods.objects.all()
# for good in goods:
# json_dict = {}
# #获取商品的每个字段,键值对形式
# json_dict['name'] = good.name
# json_dict['category'] = good.category.name
# json_dict['market_price'] = good.market_price
# json_list.append(json_dict)
from django.forms.models import model_to_dict
for good in goods:
json_dict = model_to_dict(good)
json_list.append(json_dict)
from django.http import HttpResponse
import json
#返回json,一定要指定类型content_type='application/json'
return HttpResponse(json.dumps(json_list),content_type='application/json')
但是这样有个问题,就是ImageFieldFile 和add_time字段不能序列化
如何才能将所有字段序列化呢?就要用到django的serializers
(2)django serializer的用法
# goods/view_base.py
from django.views.generic import View
from goods.models import Goods
class GoodsListView(View):
def get(self,request):
#通过django的view实现商品列表页
json_list = []
#获取所有商品
goods = Goods.objects.all()
# for good in goods:
# json_dict = {}
# #获取商品的每个字段,键值对形式
# json_dict['name'] = good.name
# json_dict['category'] = good.category.name
# json_dict['market_price'] = good.market_price
# json_list.append(json_dict)
import json
from django.core import serializers
from django.http import JsonResponse
json_data = serializers.serialize('json',goods)
json_data = json.loads(json_data)
#In order to allow non-dict objects to be serialized set the safe parameter to False.
return JsonResponse(json_data,safe=False)
django的serializer虽然可以很简单实现序列化,但是有几个缺点
- 字段序列化定死的,要想重组的话非常麻烦
- 从上面截图可以看出来,images保存的是一个相对路径,我们还需要补全路径,而这些drf都可以帮助我们做到
以上写了这么多只是为了引入django rest framework和简单介绍django的序列化用法,下面就是重点讲解django rest framework了
5.3.APIview方式实现商品列表页
(1)安装
- pip install coreapi drf的文档支持
- pip install django-guardian drf对象级别的权限支持
(2)配置def文档的url
MxShop/urls.py
from rest_framework.documentation import include_docs_urls
urlpatterns = [
#drf文档,title自定义
path('docs',include_docs_urls(title='仙剑奇侠传')),
]
(3)配置rest_framework
settings.py中添加
INSTALLED_APPS = [
'rest_framework',
]
MxShop/urls.py
urlpatterns = [
path('api-auth/',include('rest_framework.urls')),
]
(4)goods文件夹下面新建serializers.py
用drf的序列化实现商品列表页展示,代码如下:
# goods/serializers.py
from rest_framework import serializers
class GoodsSerializer(serializers.Serializer):
name = serializers.CharField(required=True,max_length=100)
click_num = serializers.IntegerField(default=0)
goods_front_image = serializers.ImageField()
(5)goods/views.py
# googd/views.py
from rest_framework.views import APIView
from goods.serializers import GoodsSerializer
from .models import Goods
from rest_framework.response import Response
class GoodsListView(APIView):
'''
商品列表
'''
def get(self,request,format=None):
goods = Goods.objects.all()
goods_serialzer = GoodsSerializer(goods,many=True)
return Response(goods_serialzer.data)
5.4.drf的Modelserializer实现商品列表页
上面是用Serializer实现的,需要自己手动添加字段,如果用Modelserializer,会更加的方便,直接用__all__就可以全部序列化
# goods/serializers.py
from rest_framework import serializers
from .models import Goods
#Serializer实现商品列表页
# class GoodsSerializer(serializers.Serializer):
# name = serializers.CharField(required=True,max_length=100)
# click_num = serializers.IntegerField(default=0)
# goods_front_image = serializers.ImageField()
#ModelSerializer实现商品列表页
class GoodsSerializer(serializers.ModelSerializer):
class Meta:
model = Goods
fields = '__all__'
category只显示分类的id,Serialzer还可以嵌套使用,覆盖外键字段
# goods/serializers.py
from rest_framework import serializers
from .models import Goods,GoodsCategory
#Serializer实现商品列表页
# class GoodsSerializer(serializers.Serializer):
# name = serializers.CharField(required=True,max_length=100)
# click_num = serializers.IntegerField(default=0)
# goods_front_image = serializers.ImageField()
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = GoodsCategory
fields = "__all__"
#ModelSerializer实现商品列表页
class GoodsSerializer(serializers.ModelSerializer):
#覆盖外键字段
category = CategorySerializer()
class Meta:
model = Goods
fields = '__all__'
5.5.GenericView实现商品列表页
(1)mixins和generic一起用用
GenericAPIView继承APIView,封装了很多方法,比APIView功能更强大
class GenericAPIView(views.APIView):
"""
Base class for all other generic views.
"""
# You'll need to either set these attributes,
# or override `get_queryset()`/`get_serializer_class()`.
# If you are overriding a view method, it is important that you call
# `get_queryset()` instead of accessing the `queryset` property directly,
# as `queryset` will get evaluated only once, and those results are cached
# for all subsequent requests.
queryset = None
serializer_class = None
# If you want to use object lookups other than pk, set 'lookup_field'.
# For more complex lookup requirements override `get_object()`.
lookup_field = 'pk'
lookup_url_kwarg = None
# The filter backend classes to use for queryset filtering
filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
# The style to use for queryset pagination.
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
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
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
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['context'] = self.get_serializer_context()
return serializer_class(*args, **kwargs)
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
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
return {
'request': self.request,
'format': self.format_kwarg,
'view': self
}
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
@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
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)
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)
用的时候需要定义queryset和serializer_class
GenericAPIView里面默认为空
- queryset = None
- serializer_class = None
ListModelMixin里面list方法帮我们做好了分页和序列化的工作,只要调用就好了
class 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)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
实现如下:
from goods.serializers import GoodsSerializer
from .models import Goods
from rest_framework.response import Response
from rest_framework import mixins
from rest_framework import generics
class GoodsListView(mixins.ListModelMixin,generics.GenericAPIView):
'商品列表页'
queryset = Goods.objects.all()
serializer_class = GoodsSerializer
def get(self,request,*args,**kwargs):
return self.list(request,*args,**kwargs)
上面的代码优化,可以直接继承ListAPIView,ListAPIView主要做了两件事:
- ListAPIView(mixins.ListModelMixin,GenericAPIView) 继承了这两个类
- 写好了get方法
我们要获取商品列表页的信息,只要写三行代码就可以了
class GoodsListView(generics.ListAPIView):
'商品列表页'
queryset = Goods.objects.all()
serializer_class = GoodsSerializer
5.6.添加分页功能
先看rest_framework/settings.py源码,里面可以找到如何配置:比如认证、权限和分页等等
"""
Settings for REST framework are all namespaced in the REST_FRAMEWORK setting.
For example your project's `settings.py` file might look like this:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.TemplateHTMLRenderer',
)
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
)
}
This module provides the `api_setting` object, that is used to access
REST framework settings, checking for user settings first, then falling
back to the defaults.
"""
from __future__ import unicode_literals
from importlib import import_module
from django.conf import settings
from django.test.signals import setting_changed
from django.utils import six
from rest_framework import ISO_8601
DEFAULTS = {
# Base API policies
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
),
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
),
'DEFAULT_THROTTLE_CLASSES': (),
'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation',
'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata',
'DEFAULT_VERSIONING_CLASS': None,
# Generic view behavior
'DEFAULT_PAGINATION_CLASS': None,
'DEFAULT_FILTER_BACKENDS': (),
# Schema
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',
# Throttling
'DEFAULT_THROTTLE_RATES': {
'user': None,
'anon': None,
},
'NUM_PROXIES': None,
# Pagination
'PAGE_SIZE': None,
# Filtering
'SEARCH_PARAM': 'search',
'ORDERING_PARAM': 'ordering',
# Versioning
'DEFAULT_VERSION': None,
'ALLOWED_VERSIONS': None,
'VERSION_PARAM': 'version',
# Authentication
'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
'UNAUTHENTICATED_TOKEN': None,
# View configuration
'VIEW_NAME_FUNCTION': 'rest_framework.views.get_view_name',
'VIEW_DESCRIPTION_FUNCTION': 'rest_framework.views.get_view_description',
# Exception handling
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
'NON_FIELD_ERRORS_KEY': 'non_field_errors',
# Testing
'TEST_REQUEST_RENDERER_CLASSES': (
'rest_framework.renderers.MultiPartRenderer',
'rest_framework.renderers.JSONRenderer'
),
'TEST_REQUEST_DEFAULT_FORMAT': 'multipart',
# Hyperlink settings
'URL_FORMAT_OVERRIDE': 'format',
'FORMAT_SUFFIX_KWARG': 'format',
'URL_FIELD_NAME': 'url',
# Input and output formats
'DATE_FORMAT': ISO_8601,
'DATE_INPUT_FORMATS': (ISO_8601,),
'DATETIME_FORMAT': ISO_8601,
'DATETIME_INPUT_FORMATS': (ISO_8601,),
'TIME_FORMAT': ISO_8601,
'TIME_INPUT_FORMATS': (ISO_8601,),
# Encoding
'UNICODE_JSON': True,
'COMPACT_JSON': True,
'STRICT_JSON': True,
'COERCE_DECIMAL_TO_STRING': True,
'UPLOADED_FILES_USE_URL': True,
# Browseable API
'HTML_SELECT_CUTOFF': 1000,
'HTML_SELECT_CUTOFF_TEXT': "More than {count} items...",
# Schemas
'SCHEMA_COERCE_PATH_PK': True,
'SCHEMA_COERCE_METHOD_NAMES': {
'retrieve': 'read',
'destroy': 'delete'
},
}
# List of settings that may be in string import notation.
IMPORT_STRINGS = (
'DEFAULT_RENDERER_CLASSES',
'DEFAULT_PARSER_CLASSES',
'DEFAULT_AUTHENTICATION_CLASSES',
'DEFAULT_PERMISSION_CLASSES',
'DEFAULT_THROTTLE_CLASSES',
'DEFAULT_CONTENT_NEGOTIATION_CLASS',
'DEFAULT_METADATA_CLASS',
'DEFAULT_VERSIONING_CLASS',
'DEFAULT_PAGINATION_CLASS',
'DEFAULT_FILTER_BACKENDS',
'DEFAULT_SCHEMA_CLASS',
'EXCEPTION_HANDLER',
'TEST_REQUEST_RENDERER_CLASSES',
'UNAUTHENTICATED_USER',
'UNAUTHENTICATED_TOKEN',
'VIEW_NAME_FUNCTION',
'VIEW_DESCRIPTION_FUNCTION'
)
# List of settings that have been removed
REMOVED_SETTINGS = (
"PAGINATE_BY", "PAGINATE_BY_PARAM", "MAX_PAGINATE_BY",
)
def perform_import(val, setting_name):
"""
If the given setting is a string import notation,
then perform the necessary import or imports.
"""
if val is None:
return None
elif isinstance(val, six.string_types):
return import_from_string(val, setting_name)
elif isinstance(val, (list, tuple)):
return [import_from_string(item, setting_name) for item in val]
return val
def import_from_string(val, setting_name):
"""
Attempt to import a class from a string representation.
"""
try:
# Nod to tastypie's use of importlib.
module_path, class_name = val.rsplit('.', 1)
module = import_module(module_path)
return getattr(module, class_name)
except (ImportError, AttributeError) as e:
msg = "Could not import '%s' for API setting '%s'. %s: %s." % (val, setting_name, e.__class__.__name__, e)
raise ImportError(msg)
class APISettings(object):
"""
A settings object, that allows API settings to be accessed as properties.
For example:
from rest_framework.settings import api_settings
print(api_settings.DEFAULT_RENDERER_CLASSES)
Any setting with string import paths will be automatically resolved
and return the class, rather than the string literal.
"""
def __init__(self, user_settings=None, defaults=None, import_strings=None):
if user_settings:
self._user_settings = self.__check_user_settings(user_settings)
self.defaults = defaults or DEFAULTS
self.import_strings = import_strings or IMPORT_STRINGS
self._cached_attrs = set()
@property
def user_settings(self):
if not hasattr(self, '_user_settings'):
self._user_settings = getattr(settings, 'REST_FRAMEWORK', {})
return self._user_settings
def __getattr__(self, attr):
if attr not in self.defaults:
raise AttributeError("Invalid API setting: '%s'" % attr)
try:
# Check if present in user settings
val = self.user_settings[attr]
except KeyError:
# Fall back to defaults
val = self.defaults[attr]
# Coerce import strings into classes
if attr in self.import_strings:
val = perform_import(val, attr)
# Cache the result
self._cached_attrs.add(attr)
setattr(self, attr, val)
return val
def __check_user_settings(self, user_settings):
SETTINGS_DOC = "http://www.django-rest-framework.org/api-guide/settings/"
for setting in REMOVED_SETTINGS:
if setting in user_settings:
raise RuntimeError("The '%s' setting has been removed. Please refer to '%s' for available settings." % (setting, SETTINGS_DOC))
return user_settings
def reload(self):
for attr in self._cached_attrs:
delattr(self, attr)
self._cached_attrs.clear()
if hasattr(self, '_user_settings'):
delattr(self, '_user_settings')
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
def reload_api_settings(*args, **kwargs):
setting = kwargs['setting']
if setting == 'REST_FRAMEWORK':
api_settings.reload()
setting_changed.connect(reload_api_settings)
添加分页功能,配置如下:
REST_FRAMEWORK = {
#分页
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
#每页显示的个数
'PAGE_SIZE': 10,
}
自定也分页功能
from rest_framework.pagination import PageNumberPagination
class GoodsPagination(PageNumberPagination):
'''
商品列表自定义分页
'''
#默认每页显示的个数
page_size = 10
#可以动态改变每页显示的个数
page_size_query_param = 'page_size'
#页码参数
page_query_param = 'page'
#最多能显示多少页
max_page_size = 100
class GoodsListView(generics.ListAPIView):
'商品列表页'
pagination_class = GoodsPagination #分页
queryset = Goods.objects.all()
serializer_class = GoodsSerializer
settings.py里面就不用设置了
5.7.viewsets和router完成商品列表页
主要用到viewsets中的GenericViewSet
再看下ViewSerMixin源码
class ViewSetMixin(object):
"""
This is the magic.
Overrides `.as_view()` so that it takes an `actions` keyword that performs
the binding of HTTP methods to actions on the Resource.
For example, to create a concrete view binding the 'GET' and 'POST' methods
to the 'list' and 'create' actions...
view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
"""
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
"""
Because of the way class based views create a closure around the
instantiated view, we need to totally reimplement `.as_view`,
and slightly modify the view function that is created and returned.
"""
# The suffix initkwarg is reserved for displaying the viewset type.
# eg. 'List' or 'Instance'.
cls.suffix = None
# The detail initkwarg is reserved for introspecting the viewset type.
cls.detail = None
# Setting a basename allows a view to reverse its action urls. This
# value is provided by the router through the initkwargs.
cls.basename = None
# actions must not be empty
if not actions:
raise TypeError("The `actions` argument must be provided when "
"calling `.as_view()` on a ViewSet. For example "
"`.as_view({'get': 'list'})`")
# sanitize keyword arguments
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r" % (
cls.__name__, key))
def view(request, *args, **kwargs):
self = cls(**initkwargs)
# We also store the mapping of request methods to actions,
# so that we can later set the action attribute.
# eg. `self.action = 'list'` on an incoming GET request.
self.action_map = actions
# Bind methods to actions
# This is the bit that's different to a standard view
for method, action in actions.items():
handler = getattr(self, action)
setattr(self, method, handler)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
# And continue as usual
return self.dispatch(request, *args, **kwargs)
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
# We need to set these on the view function, so that breadcrumb
# generation can pick out these bits of information from a
# resolved URL.
view.cls = cls
view.initkwargs = initkwargs
view.suffix = initkwargs.get('suffix', None)
view.actions = actions
return csrf_exempt(view)
def initialize_request(self, request, *args, **kwargs):
"""
Set the `.action` attribute on the view, depending on the request method.
"""
request = super(ViewSetMixin, self).initialize_request(request, *args, **kwargs)
method = request.method.lower()
if method == 'options':
# This is a special case as we always provide handling for the
# options method in the base `View` class.
# Unlike the other explicitly defined actions, 'metadata' is implicit.
self.action = 'metadata'
else:
self.action = self.action_map.get(method)
return request
def reverse_action(self, url_name, *args, **kwargs):
"""
Reverse the action for the given `url_name`.
"""
url_name = '%s-%s' % (self.basename, url_name)
kwargs.setdefault('request', self.request)
return reverse(url_name, *args, **kwargs)
@classmethod
def get_extra_actions(cls):
"""
Get the methods that are marked as an extra ViewSet `@action`.
"""
return [method for _, method in getmembers(cls, _is_extra_action)]
ViewSets和Routers结合使用
MxShop/yrls.py
from goods.views import GoodsListViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
#配置goods的url
router.register(r'goods', GoodsListViewSet)
urlpatterns = [
#商品列表页
re_path('^', include(router.urls)),
]
views.py
必须定义一个默认的排序方式
class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
'商品列表页'
# 分页
pagination_class = GoodsPagination
#这里必须要定义一个默认的排序,否则会报错
queryset = Goods.objects.all().order_by('id')
serializer_class = GoodsSerializer
5.8.drf的APIView、GenericView、viewsets和router的原理分析
genericViewSet 是最高的一层
往下
GenericViewSet(viewsets) ----drf
GenericAPIView ---drf
APIView ---drf
View ----django
这些view功能的不同,主要的是有mixin的存在
mixins总共有五种:
CreateModelMixin
ListModelMixin
UpdateModelMixin
RetrieveModelMixin
DestoryModelMixin
"""
Basic building blocks for generic class based views.
We don't bind behaviour to http method handlers yet,
which allows mixin classes to be composed in interesting ways.
"""
from __future__ import unicode_literals
from rest_framework import status
from rest_framework.response import Response
from rest_framework.settings import api_settings
class 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 {}
class 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)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
class 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)
class 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)
class 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()
以ListModelMixin为例:
如果不继承ListModelMixin的话,就无法将get和商品的列表关联起来,另外还有其中的分页等等,都无法实现。
还有其它几个mixin(增删改查局部),这些功能都是mixin做的
我们一般都是用viewsets
ViewSet类与View类其实几乎是相同的,但提供的是read或update这些操作,而不是get或put 等HTTP动作。同时,ViewSet为我们提供了默认的URL结构, 使得我们能更专注于API本身。
Router提供了一种简单,快速,集成的方式来定义一系列的urls
- 移动商城项目总结
- 移动商城第一篇【搭建项目环境+数据模型】
- 移动商城第二篇(品牌管理模块)【文件上传、数据校验、CRUD】
- 手把手在亚马逊EC2上搭建Keras GPU
- 移动商城第三篇(商品管理)【查询商品、添加商品】
- 移动商城第四篇(商品管理)【添加商品续篇、商品审核和上下架、前台展示、后台筛选】
- 移动商城第五篇(用户模块)【用户登陆、回显用户、拦截器、收货地址】
- 移动商城第六篇【单品查询、静态化页面】
- 移动商城第七篇【购物车增删改查、提交订单】
- Shiro入门这篇就够了【Shiro的基础知识、回顾URL拦截】
- OFTest(一):如何忽略一些字段在端口poll报文
- Shiro第二篇【授权、整合Spirng、过滤器】
- Ajax数据的爬取(淘女郎为例)
- 在IDEA中编写Spark的WordCount程序
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Python装饰器实现函数动态类型检查
- 如何自动填充SQL语句中的公共字段
- 面试官喜欢问的Java编译期与运行期问题总结全了
- 面试5年25K程序员回答不出来为什么Java的main方法必须是public static void?
- Python装饰器
- 3分钟短文:十年窖藏,Laravel告诉你表单验证的“正确姿势”
- 面试官:说说SpringBoot中Spring容器的启动过程
- Python IO
- Python解析式
- 面试官:Tomcat 的调优怎么做?你的最佳实践有哪些?
- Tomcat源码分析
- lazy-mock ,一个生成后端模拟数据的懒人工具
- Python拉链法和开地址法实现字典
- Springboot2 整合redis发布订阅 解决订阅多个频道重复代码过多 创建很多bean问题
- 爬虫选择器算法漫谈