内容概览
- 路由
- 登录接口编写
- 认证
路由
只要继承了ViewSetMixin及其子类的视图类,都可以自动生成路由
自动生成路由
"""1. 在urls.py中导入模块"""
from rest_framework.routers import SimpleRouter, DefaultRouter # 这两个的差别在于根路由路径下是否有页面
"""2. 实例化得到对象"""
router = SimpleRouter()
"""3. 注册"""
router.register('user', views.User, 'user') # register(self, prefix, viewset, basename=None) 第一个参数是前缀,也就是路径;第二个参数是继承了ViewSetMixin及其子类的视图类;第三个参数是别名,不写默认就是第一个参数0
"""4. 添加到urlpatterns中"""
1. urlpatterns+=router.urls # 或者urlpatterns.extend(router.urls)
2. path('', include(router.urls)) # 使用路由分发
"""
自动生成路由后,路由的映射关系也就固定了
get=list/retrieve
post=create
put=update
...
如果以后写的视图类不写action装饰器的话,那么视图类中必须要有list,create,destroy,update,retrieve其中之一
"""
action装饰器的使用
在视图函数中,使用其他名字的方法,需要使用action装饰器做映射关系来确定什么请求执行该方法
from rest_framework.decorators import action
@action(methods=['GET', ], detail=False, url_path='login', url_name='login')
def login(self, request):
"""
methods: 指定请求方式
detail: 默认为False,控制路由中是否携带pk
url_path: 控制路由后缀是什么,默认以方法名命名
url_name: 起别名,用于反向解析
"""
添加了装饰器后就可以自动生成路由
登录接口编写
- models.py
class User(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
def __str__(self):
return self.username
"""使用一张表来一对一记录用户的登录状态"""
class UserToken(models.Model):
user = models.OneToOneField(to='User', on_delete=models.CASCADE)
token = models.CharField(max_length=64, null=True) # 如果用户没有登录为空,登录过就有值,再次登录更新值
- views.py
class UserView(ViewSet):
@action(['POST', ], detail=False, url_path='login', url_name='login')
def login(self, request):
username = request.POST.get('username')
password = request.POST.get('password')
user_obj = models.User.objects.filter(username=username, password=password).first() # 取出前端传入的数据并校验
if user_obj:
token = uuid.uuid4() # 获取一串随机字符串
models.UserToken.objects.update_or_create({'token':token},user=user_obj) # 登录成功就将随机字符串保存或更新到表中
# update_or_create:没有新建,有则更新
return JsonResponse({'code': 100, 'msg': '登录成功'}, 'token': token)
else:
return JsonResponse({'code': 101, 'msg': '用户名或密码错误'})
- urls.py
router = SimpleRouter()
router.register('user', views.UserView, basename='user')
urlpatterns = [
path('admin/', admin.site.urls),
path('', include(router.urls)),
]
认证
想要访问一个接口必须先登录后才能访问,可以通过认证类实现该功能
- 写一个认证类,继承BaseAuthenTication
- 重写authenticate方法,在内部做认证
- 如果认证通过,返回两个值,当前登录用户与token
- 认证不通过抛AuthenticationFailed异常
- 返回了两个值,后续的request.user就是返回的用户
- 认证类的使用方式
方式一:在视图类中添加类属性
方式二:全局配置,在setting.py中配置class UserView(ViewSet): authentication_classes = [auth.LoginAuth, ]
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES':['app01.auth.LoginAuth'] } """可以将类属性的authentication_classes = []来局部禁用"""
- 认证类
class LoginAuth(BaseAuthentication):
def authenticate(self, request):
token = request.POST.get('token')
user_token_obj = models.UserToken.objects.filter(token=token).first()
if user_token_obj: # 获取token值并获取对应数据,如果存在则用户已登录
return user_token_obj, token
else:
raise AuthenticationFailed('当前没有登录')
练习
- 登录接口,图书5个接口,出版社5个接口(关联的关系),,使用9个视图子类,5个视图扩展类写
- 图书的必须登录后才能方法,出版社的不登录就能访问
-全局配置,局部禁用
代码
- models
from django.db import models
class User(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
def __str__(self):
return self.username
class UserToken(models.Model):
user = models.OneToOneField(to='User', on_delete=models.CASCADE)
token = models.CharField(max_length=64, null=True)
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
# publish_date = models.DateField(null=True)
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
def publish_dict(self):
return {'name':self.publish.name, 'city': self.publish.city, 'email': self.publish.email}
def __str__(self):
return self.name
class Publish(models.Model):
name = models.CharField(max_length=32)
city = models.CharField(max_length=32)
email = models.EmailField()
def __str__(self):
return self.name
- urls
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import SimpleRouter, DefaultRouter
from app01 import views
router = SimpleRouter()
router.register('user', views.UserView, basename='user')
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BookView.as_view()),
path('books/<int:pk>', views.BookDitailView.as_view()),
path('publish/', views.PublishView.as_view()),
path('publish/<int:pk>', views.PublishDitailView.as_view()),
path('', include(router.urls)),
]
- serializers
from rest_framework import serializers
from app01 import models
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = ['id', 'name', 'price', 'publish', 'publish_dict']
extra_kwargs = {
'publish': {'write_only': True},
'publish_dict': {'read_only': True},
}
class PublishModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Publish
fields = '__all__'
- auth
from rest_framework.authentication import BaseAuthentication
from app01 import models
from rest_framework.exceptions import AuthenticationFailed
class LoginAuth(BaseAuthentication):
def authenticate(self, request):
token = request.GET.get('token')
user_token_obj = models.UserToken.objects.filter(token=token).first()
if user_token_obj:
return user_token_obj, token
else:
raise AuthenticationFailed('当前没有登录')
- views
import uuid
from django.shortcuts import render, HttpResponse
from django.http import JsonResponse
from rest_framework.viewsets import ViewSet
from rest_framework.generics import GenericAPIView, ListCreateAPIView, RetrieveUpdateDestroyAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, \
DestroyModelMixin
from rest_framework.decorators import action
from app01 import models
from app01 import serializers
class UserView(ViewSet):
authentication_classes = []
@action(['POST', ], detail=False, url_path='login', url_name='login')
def login(self, request):
username = request.POST.get('username')
password = request.POST.get('password')
user_obj = models.User.objects.filter(username=username, password=password).first()
if user_obj:
token = str(uuid.uuid4())
models.UserToken.objects.update_or_create({'token': token}, user=user_obj)
return JsonResponse({'code': 100, 'msg': '登录成功'})
else:
return JsonResponse({'code': 101, 'msg': '用户名或密码错误'})
class BookView(GenericAPIView, ListModelMixin, CreateModelMixin):
queryset = models.Book.objects.all()
serializer_class = serializers.BookModelSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class BookDitailView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
queryset = models.Book.objects.all()
serializer_class = serializers.BookModelSerializer
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 delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
class PublishView(ListCreateAPIView):
queryset = models.Publish.objects.all()
serializer_class = serializers.PublishModelSerializer
authentication_classes = []
class PublishDitailView(RetrieveUpdateDestroyAPIView):
queryset = models.Publish.objects.all()
serializer_class = serializers.PublishModelSerializer
authentication_classes = []
研究一下级联删除的其他
CASCADE:删除关联数据的时候,与之关联的也一同删除
DO_NOTHING:删除关联数据的时候,与之关联的不做操作
PROTECT:删除关联数据的时候,直接报错
SET_NULL:删除关联数据的时候,与之关联的值设置为空(定义外键时需要设置可以为空)
SET_DEFAULT:删除关联数据的时候,与之关联的值设置为默认值(定义外键时需要设置默认值)
SET():删除关联数据的时候,与之关联的值设置为括号内自定义的值