我们已经知道,drf 对原生的 request 请求数据对象做过封装处理,原生 request 对象被保存在_request 中,通过 __getattr__【点拦截】魔法方法可以获取 原生 request 对象所有方法和属性,并且 drf 的 request 可以通过 request.data 取出包括 json 格式数据在内的所有编码格式,使得数据处理更加方便。
导入模块 Request
from rest_framework.request import Request
request 对象需要记忆:
__getattr__request.datarequest.query_parmasrequest.query_parmas 源码:接受原生 request 对象的GET 请求的数据,也就是过滤参数
@property
def query_params(self):
"""
More semantically correct name for request.GET.
"""
return self._request.GET
限制
request能接收的数据格式
局部配置:
在视图层的视图类中配置,限制 request 对象只能处理json格式数据,只针对当前视图类有效
from rest_framework.parsers import JSONParser,FormParser,MultiPartParser
class PublishView(APIView):
# 局部使用,只针对当前视图类有效,只想处理json格式
parser_classes = [JSONParser]
def get(self, request):
pass
全局配置:
在项目文件夹下的 settings.py文件中配置
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser',
],
}
解析顺序:
视图层类中的配置 > 项目文件夹下 settings.py 文件配置 > drf 原有的默认配置
drf的默认配置:from rest_framework import settings

from rest_framework.response import Response
形参:
data=None, # 字符串,字典,列表--》给http响应body体中内容-->response对象中取出处理
status=None, # 响应状态码:1xx,2xx,3xx,默认是200
from rest_framework.status import HTTP_201_CREATED
Response(ser.data,status=HTTP_201_CREATED)
headers=None, # 响应头 字典
---了解---
template_name=None, # 模板名字(不用),用浏览器访问时,可以改
exception=False, # 异常处理
content_type=None # 响应编码格式
设置响应的数据格式:
局部设置:
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
class BookDetailView(APIView):
renderer_classes = [JSONRenderer,]
全局设置:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类
'rest_framework.renderers.JSONRenderer', # json渲染器
'rest_framework.renderers.BrowsableAPIRenderer', # 浏览API渲染器
)
}
GenericAPIView 继承的是 APIView
前面我们已经知道 APIView 对原生 request 对象进行了处理,新的request对象多了 .data 属性,并且有三大认证和全局异常处理。
APIView还拥有限制请求和响应数据的属性 renderer_classes,parser_classes
那么,GenericAPIView 就是在 APIView 的基础上,多了更多属性和方法
from rest_framework.generics import GenericAPIView
更多属性和方法可以查看 GenericAPIView 源码
属性:
class GenericAPIView(views.APIView):
queryset = None
serializer_class = None
lookup_field = 'pk'
lookup_url_kwarg = None
filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
方法:

优先记忆:
属性
queryset = None : 获取模型类对象serializer_class = None : 获取序列化类的属性lookup_field = 'pk' : 获取单个对象时需要用到改属性方法:
get_queryset :获取模型类对象 querysetget_object : 获取单个模型类对象get_serializer :获取序列化类执行并返回一个序列化后的对象get_serializer_class : 获取序列化类get_queryset
def get_queryset(self):
# 断言 self.queryset 不为空,否则报错后面信息,说明 queryset 属性必须定义
assert self.queryset is not None, (
"'%s' should either include a `queryset` attribute, "
"or override the `get_queryset()` method."
% self.__class__.__name__
)
# 获取 queryset 属性并放回
queryset = self.queryset
if isinstance(queryset, QuerySet):
queryset = queryset.all()
return queryset
get_serializer
def get_serializer(self, *args, **kwargs):
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):
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_object
def get_object(self):
queryset = self.filter_queryset(self.get_queryset())
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)
self.check_object_permissions(self.request, obj)
return obj
不是视图类,没有继承APIView,需要配合GenericAPIView 使用,这五个类中提供了查找、新增、修改删除模型类数据的方法,在GenericAPIView类的五个接口中可以直接调用
from rest_framework.mixins import CreateModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin
class BookAPIView(APIView):
def get(self, request):
book_list = models.Book.objects.all()
ser = BookModelSerializer(instance=book_list, many=True) # 获取多个资源用 many=True
print(ser.data)
return Response(ser.data)
def post(self, request):
ser = BookModelSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response({'code': 100, 'msg': '新增成功', 'data': ser.data})
return Response({'code': 101, 'msg': '新增失败', 'data': ser.errors})
class BookSetAPIView(APIView):
def get(self, request, pk):
book_obj = models.Book.objects.filter(pk=pk).first()
ser = BookModelSerializer(instance=book_obj)
return Response(ser.data)
def put(self, request, pk):
book_obj = models.Book.objects.filter(pk=pk).first()
ser = BookModelSerializer(instance=book_obj, data=request.data) # 修改是既有instance 也有data才行
if ser.is_valid():
ser.save()
return Response({'status': 100, 'msg': '修改成功', 'data': ser.data})
return Response({'status': 100, 'msg': '修改失败', 'data': ser.errors})
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
return Response({'status': 100, 'msg': '删除成功'})
和继承 APIView 的区别就是,多了queryset、serializer_class属性,还有get_queryset等方法,而且无论换了什么模型表(Auth、Publish),只需要改变queryset、serializer_class属性即可,五个接口方法均不需要改变
class BookGeneric(GenericAPIView):
queryset = Book.objects
serializer_class = BookModelSerializer
def get(self, request):
quertset = self.get_queryset()
serializer_class = self.get_serializer(instance=quertset, many=True)
print(serializer_class.data)
return Response(serializer_class.data)
def post(self, request):
serializer_class = self.get_serializer(data=request.data)
if serializer_class.is_valid():
serializer_class.save()
return Response({'code': 100, 'msg': '新增成功', 'data': serializer_class.data})
return Response({'code': 101, 'msg': '新增失败', 'data': serializer_class.errors})
class BookSetGeneric(GenericAPIView):
queryset = Book.objects
serializer_class = BookModelSerializer
def get(self, request, pk):
queryset = self.get_object()
serializer_class = self.get_serializer(instance=queryset)
return Response(serializer_class.data)
def put(self, request, pk):
queryset = self.get_object()
serializer_class = self.get_serializer(instance=queryset, data=request.data) # 修改是既有instance 也有data才行
if serializer_class.is_valid():
serializer_class.save()
return Response({'status': 100, 'msg': '修改成功', 'data': serializer_class.data})
return Response({'status': 100, 'msg': '修改失败', 'data': serializer_class.errors})
def delete(self, request, pk):
self.get_object().delete()
return Response({'status': 100, 'msg': '删除成功'})
五个视图扩展类封装了原来 GeneriacAPIView 类所需要书写的五个接口方法
获取模型类所有数据对象(get方法)和修改数据(post方法),书写的方法和原来 GeneriacAPIView 类中的五个接口几乎一致:
class ListModelMixin:
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 CreateModelMixin:
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)
获取单个模型类数据对象、修改数据、删除数据三个需要传入pk值的视图扩展类:
class RetrieveModelMixin:
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
class UpdateModelMixin:
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):
instance._prefetched_objects_cache = {}
return Response(serializer.data)
class DestroyModelMixin:
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
self.perform_destroy(instance)
return Response(status=status.HTTP_204_NO_CONTENT)
视图层类:
继承了这些视图扩展类,我们就可以使用类中封装的接口函数,更进一步精简视图层类的接口代码了
class BookGenericMinxin(GenericAPIView, ListModelMixin, CreateModelMixin):
queryset = Book.objects
serializer_class = BookModelSerializer
def get(self, request):
return self.list(request)
def post(self, request):
return self.create(request)
class BookGenericSetMinxin(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
queryset = Book.objects
serializer_class = BookModelSerializer
def get(self, request, pk):
return self.retrieve(request)
def put(self, request, pk):
return self.update(request)
def delete(self, request, pk):
return self.destroy(request)
序列化类:
class BookModelSerializer(ModelSerializer): # 继承的是 ModelSerializer
class Meta:
model = Book
fields = ['id', 'name', 'price', 'publish', 'authors', 'author_list', 'publish_info'] # 注意别漏写 fields
extra_kwargs = {
'publish': {'write_only': True},
'authors': {'write_only': True}
}
路由层:
urlpatterns = [
path('admin/', admin.site.urls),
path('book/', views.BookAPIView.as_view()),
path('book/<int:pk>/', views.BookSetAPIView.as_view()),
path('book1/', views.BookGeneric.as_view()),
path('book1/<int:pk>/', views.BookSetGeneric.as_view()),
path('book2/', views.BookGenericMinxin.as_view()),
path('book2/<int:pk>/', views.BookGenericSetMinxin.as_view()),
]
封装程度越来越高,代码越来越精简
这九个视图子类,连 get、post、put、get、delete 五个接口都帮我们写完了,我们在书写GenericAPIView视图层类时,只需要导入这九个试图子类来继承,就可以不用书写五个方法了,需要什么接口导入什么模块。
单独的类:
from rest_framework.generics import CreateAPIView, ListAPIView, RetrieveAPIView, UpdateAPIView, DestroyAPIView
组合:
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView, RetrieveDestroyAPIView, RetrieveUpdateAPIView
CreateAPIView
ListAPIView
RetrieveAPIView
UpdateAPIView
DestroyAPIView
ListCreateAPIView
RetrieveUpdateDestroyAPIView
RetrieveDestroyAPIView
RetrieveUpdateAPIView
其实,它们就是继承了GenericAPIView类,并且与五个视图扩展类进行了组合继承,并把接口函数也添加上罢了。pk使用 *args 和 **kwargs 来取缔了
class CreateAPIView(mixins.CreateModelMixin,
GenericAPIView):
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class ListAPIView(mixins.ListModelMixin,
GenericAPIView):
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
class RetrieveAPIView(mixins.RetrieveModelMixin,
GenericAPIView):
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
class UpdateAPIView(mixins.UpdateModelMixin,
GenericAPIView):
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)
class DestroyAPIView(mixins.DestroyModelMixin,
GenericAPIView):
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
# 组合的就不逐一列举了,原理一样
class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
GenericAPIView):
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)
这样,我们在书写视图层类更加精简了:
class BookView(ListCreateAPIView):
queryset = Book.objects
serializer_class = BookModelSerializer
class BookSetView(RetrieveUpdateDestroyAPIView):
queryset = Book.objects
serializer_class = BookModelSerializer
路由书写:
urlpatterns = [
path('book3/', views.BookView.as_view()),
path('book3/<int:pk>/', views.BookSetView.as_view()),
]
由ViewSetMixin的源码可以看到,它把as_view方法进行了重写,在路由层调用视图类时,如果类第一个继承的时ViewSetMixin,会优先调用它的as_view方法,需要传入位置参数 (一个字典)actions,而且由actions.items(),actions是一个字典。
这样一来我们不使用到 九个视图子类给我们封装的五个接口方法(get、post、put、delete、patch),可以直接继承 ViewSetMixin, ListModelMixin, CreateModelMixin, GenericAPIView。因为在路由中会传进来 get 等五个字典key,赋值对象地址后可直接调用。
记住:继承ViewSetMixin 之后书写路由时需要传入 actions 参数
class ViewSetMixin:
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
def view(request, *args, **kwargs):
self = cls(**initkwargs)
# 例子 method = 'get',action='list'
for method, action in actions.items():
# 获取 list() 方法的对象地址
handler = getattr(self, action)
# 相当于 self.get = list,把lsit方法赋值给了self.get,发送get请求时触发,相当于触发了 ListModelMixin 的 list() 方法
setattr(self, method, handler)
return self.dispatch(request, *args, **kwargs)
view.actions = actions
return csrf_exempt(view)
导入
from rest_framework.viewsets import ViewSetMixin
路由层:
as_view()需要传入字典:{“请求方式”: “方法名”}
注意字典中分别是五个接口的请求方式和对应的五个视图扩展类封装好的方法
urlpatterns = [
path('book4/', views.BookViewSet.as_view(actions={'get': 'list', 'post': 'create'})),
path('book4/<int:pk>/', views.BookViewSetPk.as_view(actions={'get': 'retrieve', 'put': 'update', 'delete': 'delete'}))
]
视图层类:
class BookViewSet(ViewSetMixin, ListCreateAPIView):
queryset = Book.objects
serializer_class = BookModelSerializer
class BookViewSetPk(ViewSetMixin, RetrieveUpdateDestroyAPIView):
queryset = Book.objects
serializer_class = BookModelSerializer
Viewset系列from rest_framework.viewsets import ViewSetMixin, ViewSet, GenericViewSet, ModelViewSet
ViewSet
继承了 ViewSetMixin和APIView
class ViewSet(ViewSetMixin, views.APIView):
"""
The base ViewSet class does not provide any actions by default.
"""
pass
GenericViewSet
继承了 ViewSetMixin和GenericAPIView
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
"""
The GenericViewSet class does not provide any actions by default,
but does include the base set of generic view behavior, such as
the `get_object` and `get_queryset` methods.
"""
pass
ModelViewsetMinx
包含了五个视图扩展类和 GenericViewSet
这样继承了 ModelViewSet的试图层类可以不用把五个接口分开两个类来写了
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
A viewset that provides default `create()`, `retrieve()`, `update()`,
`partial_update()`, `destroy()` and `list()` actions.
"""
pass
class BookViewSet(ModelViewSet):
queryset = Book.objects
serializer_class = BookModelSerializer
继承了 ViewSetMixin 之后,路由就要都要写成这样:
urlpatterns = [
path('book4/', views.BookViewSet.as_view(actions={'get': 'list', 'post': 'create'})),
path('book4/<int:pk>/', views.BookViewSetPk.as_view(actions={'get': 'retrieve', 'put': 'update', 'delete': 'delete'}))
]
drf 提供了自动生成这样两条路由与视图函数对应关系的功能模块 routers
用法:
# 导入
from rest_framework.routers import DefaultRouter, SimpleRouter
# 实例化对象并注册路由
router = DefaultRouter()
router.register('book4', views.BookViewSet, 'book4')
添加到路由列表中
1、路由分发
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', include(router.urls)),
]
2、直接列表相加
urlpatterns += router.urls

运行有问题或需要源码请点赞关注收藏后评论区留言一、利用ContentResolver读写联系人在实际开发中,普通App很少会开放数据接口给其他应用访问。内容组件能够派上用场的情况往往是App想要访问系统应用的通讯数据,比如查看联系人,短信,通话记录等等,以及对这些通讯数据及逆行增删改查。首先要给AndroidMaifest.xml中添加响应的权限配置 下面是往手机通讯录添加联系人信息的例子效果如下分成三个步骤先查出联系人的基本信息,然后查询联系人号码,再查询联系人邮箱代码 ContactAddActivity类packagecom.example.chapter07;importandroid
我正在开发一个包含大约10个不同功能组件的Sinatra应用程序。我们希望能够将这些组件混合并匹配到应用程序的单独实例中,完全从config.yaml文件配置,如下所示:components:-route:'/chunky'component_type:FoodListercomponent_settings:food_type:baconmax_items:400-route:'places/paris'component_type:Mappercomponent_settings:latitude:48.85387273165654longitude:2.340087890625-
我正在为需要有条件地设置cookie的Rails应用编写Rack中间件组件。我目前正在尝试设置cookie。通过谷歌搜索,这似乎应该可行:classRackAppdefinitialize(app)@app=appenddefcall(env)@status,@headers,@response=@app.call(env)@response.set_cookie("foo",{:value=>"bar",:path=>"/",:expires=>Time.now+24*60*60})[@status,@headers,@response]endend它不会给出错误,但也不会设置coo
我正在linux机器上学习rubyonrails并磨练我的VIM技能(skillz?)。当我在使用C++的时候开始使用VIM时,我有一个friend有一个很棒的vimfiles文件夹,里面有很多东西可以开始使用。从头开始,vim很棒,但感觉它还可以做得更好。我目前有:vim-rubybufferexplorerxml-edit(虽然我目前没有它可以处理erb文件)我知道这只是一些更有经验的vim/ruby开发人员所拥有的东西的皮毛(包括vim.rc文件中的一次性)。在某个地方是否有一个列表(或者我们可以创建一个)使ruby(和rails)编程更有趣所需的一堆标准vim配置?是否有一
前提:当我们要修改vant组件库中Tabbar图标大小的样式(原图标是字体图标,大小由font-size控制)。 字体图标字体大小由css变量(--van-tabbar-item-icon-size)控制, 1.插槽方法结论:当你想要自定义使用插槽时,插入自己的元素,那么可以直接在当前作用域直接修改元素的样式。自定义img{height:28px}传入图片,用height属性控制图片大小,达到与字体图标相同效果2.全局定义变量结论:全局定义一个变量,覆盖它默认变量的值定义变量缺点:全局修改。 :root{--van-tabbar-item-icon-size:30px!important;/
记个笔记以免遗忘,建议还是查看Element-UI提供的官方文档学习,自己摸索比较难受官方文档:Element-UI组件TableElement-UI官网提供了许多Table格式,这里以一个带有筛选器的表格为例表格的官网显示效果:直接将官方提供的示例代码贴入.vue文件中即可使用显示的数据是通过data()方法提供的假数据。方法见下:data(){return{tableData:[{date:'2016-05-02',name:'王小虎',address:'上海市普陀区金沙江路1518弄'},{date:'2016-05-04',name:'王小虎',address:'上海市普陀区金沙江路1
先给大家看看最终效果首先我们来定义数据data(){ return{ lsit:[ 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic118.nipic.com%2Ffile%2F20161216%2F24271963_122609717000_2.jpg&refer=http%3A%2F%2Fpic118.nipic.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1656923017&t=183ece148b13b64e9dd503afd1b15c91'
我需要设计下面的视图。解释:前两个视图是卡片视图。顶部将包含下面的图表是4个瓷砖,该图像在其底部具有图像和文本问题:我能够获得这样的设计,但并不适合所有手机和屏幕类型。我怎样才能解决这个问题?代码:看答案尝试这个:您需要使用线性布局并将重量分配给两个卡布局50-50。
有些奇怪的事情发生了,我一直在阅读React文档,他们讨论了生命周期以及如何在渲染组件之前做一些事情。我正在尝试,但我尝试的一切都失败了,总是组件首先进行渲染,然后调用componenWillMount、..didMount等。在调用这些函数之后,渲染再次发生。我需要先加载数据以填充状态,因为我不希望初始状态为null,我希望它包含自初始呈现以来的数据。我正在使用Flux和Alt,这是Action@createActions(flux)classGetDealersActions{constructor(){this.generateActions('dealerDataSuccess
在ember中为组件类指定位置参数时,您必须重新打开该类(如下所示),这样它才能工作,您不能将它包含在初始声明中(至少从我所看到的示例和我自己的经验)。importEmberfrom'ember';constcomponent=Ember.Component.extend({});component.reopenClass({positionalParams:['post'],});exportdefaultcomponent;如果你在单个声明中这样做(如下所示)它将不起作用importEmberfrom'ember';exportdefaultEmber.Component.exte