DRF-(5)

内容概览

  • 路由
  • 登录接口编写
  • 认证

路由

只要继承了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)),
]

认证

想要访问一个接口必须先登录后才能访问,可以通过认证类实现该功能

  1. 写一个认证类,继承BaseAuthenTication
  2. 重写authenticate方法,在内部做认证
  3. 如果认证通过,返回两个值,当前登录用户与token
  4. 认证不通过抛AuthenticationFailed异常
  5. 返回了两个值,后续的request.user就是返回的用户
  6. 认证类的使用方式
    方式一:在视图类中添加类属性
    class UserView(ViewSet):
    	authentication_classes = [auth.LoginAuth, ]
    
    方式二:全局配置,在setting.py中配置
    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('当前没有登录')

练习

  1. 登录接口,图书5个接口,出版社5个接口(关联的关系),,使用9个视图子类,5个视图扩展类写
  2. 图书的必须登录后才能方法,出版社的不登录就能访问
    -全局配置,局部禁用
代码
  • 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():删除关联数据的时候,与之关联的值设置为括号内自定义的值
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值