@api_view()
总所周知,drf的cbv的流程是:路由匹配>视图as_view>dispatch,然后对django原生进行扩展。
今天聊一下fbv。视图函数不用api_view()装饰器就是原生django视图,加了后就是drf的视图,那么,这个装饰器中必然调用了as_view或者dispatch。
装饰器 @api_view()里面进入了 drf的as_view()
在用 django 自己的路由与函数配合时不享有drf的request封装与验证等功能
例如:
urlpatterns = [
path('hello/',views.hello),
]
普通django视图:
def hello(request):
print(request.user, request.auth) # 报错,查无此人
return JsonResponse({'data':'hello_world'})
drf的函数视图:
@api_view(('GET',))
def hello(request):
print(request.user, request.auth)
return JsonResponse({'data':'hello_world'})
api_view装饰器
@api_view(['GET'])
@authentication_classes([SessionAuthentication, BasicAuthentication])
@permission_classes([IsAuthenticated])
def example_view(request, format=None):
content = {
'user': unicode(request.user), # `django.contrib.auth.User` instance.
'auth': unicode(request.auth), # None
}
return Response(content)
action装饰器
@action(detail=False, methods=['GET'], permission_classes=[IsOwnerOrReadOnly],
authentication_classes=[UserTokenAuthentication])
def example_view(request, format=None):
content = {
'user': unicode(request.user), # `django.contrib.auth.User` instance.
'auth': unicode(request.auth), # None
}
return Response(content)
@action()在cbv中使用,主要做了mapping,覆盖了类里面的那些authentication_classes等。
@api_view()在fbv中使用,主要将基于函数的视图转换为APIView子类,即进入drf的as_view()方法。
TokenAuthentication
class TokenAuthentication(BaseAuthentication)
# 以此类为例,源码不难看懂
def authenticate(self, request)
# 此方法获取了 Authorization: Token 401f7ac837da42b97f613d789819ff93537bee6a
# 验证了Token 格式
def authenticate_credentials(self, key)
# 调用了get_model方法,默认从rest_framework.authtoken.models拿Token表
# 然后在表里取数据验证 Token,拿到对象返回元组分别被放到 request.user 和 request.auth
def get_model(self):
if self.model is not None:
return self.model
from rest_framework.authtoken.models import Token
return Token
失败会抛异常 raise exceptions.AuthenticationFailed(msg)
# 这之间有个 ugettext_lazy 惰性翻译的使用,有什么好处?
并最终 在入口函数 dispatch 中捕获异常
调用 handle_exception(self, exc) 方法将 信息Response
自己决定自定义或用自带的,我个人不喜欢用自带的,尤其是迁移它的表。
django channels
group_name 和 channel_name
- group是一个群,group_name是群名称,channel_name是一个成员。
- group_add 加群,group_discard 退群。
- 加群和退群不是下线,不是断开了websocket连接,大家还是都与服务器连接着的。
- group就是为了将消息发送给多个channel_name
- 示例 self.channel_name : specific.xHTtAqbH!rnadllzYyWck
AsyncJsonWebsocketConsumer
websocket是事件驱动的
1.建立连接2.接收消息3.发生错误4.关闭连接
对应到channels源码就三个方法
websocket_connect(self, message)
websocket_receive(self, message)
websocket_disconnect(self, message)
然后那些 receive(),receive_json(),connect(),disconnect()都只是被调用而已,所以重写它们也能完成逻辑,追根溯源直接重写websocket_receive才是最灵活的。
class AsyncJsonWebsocketConsumer(AsyncWebsocketConsumer):
"""
Variant of AsyncWebsocketConsumer that automatically JSON-encodes and decodes
messages as they come in and go out. Expects everything to be text; will
error on binary data.
"""
# websocket的接收消息事件只有一个,其余的什么receive、receive_json只是被调用而已,
# 继承了某个类,有两种选择:
# 重写被调用的方法
# 直接重写回调方法
async def receive(self, text_data=None, bytes_data=None, **kwargs):
if text_data:
await self.receive_json(await self.decode_json(text_data), **kwargs)
else:
raise ValueError("No text section for incoming WebSocket frame!")
async def receive_json(self, content, **kwargs):
"""
Called with decoded JSON content.
"""
pass
async def send_json(self, content, close=False):
"""
Encode the given content as JSON and send it to the client.
"""
await super().send(text_data=await self.encode_json(content), close=close)
@classmethod
async def decode_json(cls, text_data):
return json.loads(text_data)
@classmethod
async def encode_json(cls, content):
return json.dumps(content)