drf阶段测试题

测试题目

1 有车型(CarModel),车厂(CarFactory),经销商(Distributor)三个表,一个车厂可以生产多种车型,一个经销商可以出售多种车型,一个车型可以有多个经销商出售
          车型:车型名,车型出厂价,车厂id
          车厂:车厂名,车厂地址,联系电话
          经销商:经销商名,地址,联系电话
2 有用户表,基于django内置user表,扩展mobile字段
3 编写登陆接口,jwt方式返回token,
            格式为{status:100,msg:登陆成功,token:safasdfa}
4 所有接口(除登录外),必须登录后才能访问
5 管理员登陆后可以增,删,单查,群查,改 车型,车厂,经销商(具备所有接口权限)
6 普通用户登陆可以查看车型,车厂,经销商单条,所有(只有查看权限)
7 所有查询所有接口带分页功能
8 查询所有车型接口,可以按车型名字精准过滤

9 加分项:
        用户注册接口
        管理员有用户锁定,删除用户功能

setting基础配置

注册应用:           

INSTALLED_APPS = [
        ...
    'app01.apps.App01Config',
    'rest_framework',
    'rest_framework_simplejwt',
]

连接数据库:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'text19',
        'HOST': '127.0.0.1',
        'POST': '3306',
        'USER': 'root',
        'PASSWORD': 'JIAJIA',
    }
}

语言配置:

LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True

models建表

注意表名书写:

        1、在models表中,表名是驼峰体    CarModel、CarFactory、Distributor

        2、除表名驼峰体外,其余变量名用下划线形式      car_factory

        3、函数内绝对不能出现下划线和驼峰体混用情况

内置user表扩展字段:

        1、需把user表的扩写字段在model表中完成了才能迁移表格,不然需重建库

        2、内置user表扩展表名可以直接命名

        3、继承 AbstractUser ----> class User(AbstractUser)

        4、需配置:

AUTH_USER_MODEL = 'app01.User'

迁移表格及创建超级用户命令:

        python manage.py createsuperuser

        python manage.py makemigrations

        python manage.py migrate

from django.db import models
from django.contrib.auth.models import AbstractUser


# 车型(CarModel),车厂(CarFactory),经销商(Distributor)
# 有用户表,基于django内置user表,扩展mobile字段

class User(AbstractUser):
    mobile = models.CharField(max_length=11)


# 车型表
class CarModel(models.Model):
    name = models.CharField(max_length=32, verbose_name='车型名')
    price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='车型出厂价')
    car_factory = models.ForeignKey(to='CarFactory', on_delete=models.SET_NULL, null=True)
    distributor = models.ManyToManyField(to='Distributor')


# 车厂表
class CarFactory(models.Model):
    name = models.CharField(max_length=32, verbose_name='车厂名')
    address = models.CharField(max_length=32, verbose_name='车厂地址')
    phone = models.CharField(max_length=32, verbose_name='车厂联系电话')


# 经销商
class Distributor(models.Model):
    name = models.CharField(max_length=32, verbose_name='经销商名')
    address = models.CharField(max_length=32, verbose_name='经销商地址')
    phone = models.CharField(max_length=32, verbose_name='经销商联系电话')

jwt登陆接口

# view.py
# 编写登陆接口,jwt方式返回token
from rest_framework.response import Response
from rest_framework.generics import GenericAPIView
from .serializer import LoginSerializer

class UserView(GenericAPIView):
    authentication_classes = []  # 登录接口不用登录后再访问
    serializer_class = LoginSerializer

    def post(self, request):
        ser = self.get_serializer(data=request.data)
        if ser.is_valid():     # 会执行字段自己的校验(没有),执行局部钩子(没有),执行全局钩子(写了:校验用户,签发token)
            return Response(ser.validated_data)
        else:
            return Response({'code': 101, 'msg': '用户名或密码错误'})
# serializer.py
# jwt登录
from rest_framework import serializers
from .models import User
import re
from rest_framework.exceptions import ValidationError
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer


class LoginSerializer(serializers.Serializer):
    username = serializers.CharField()
    password = serializers.CharField()

    # 写全局钩子
    def validate(self, attrs):
        # 校验用户,签发token
        username = attrs.get('username')
        password = attrs.get('password')
        # 2  使用正则判断用用户名是邮箱,手机号还是用户名,分别去查询当前用户
        if re.match(r'^1[3-9][0-9]{9}$', username):
            user = User.objects.filter(mobile=username).first()
        elif re.match(r'^.+@.+$', username):
            user = User.objects.filter(email=username).first()
        else:
            user = User.objects.filter(username=username).first()
        if user and user.check_password(password):
            # 3 校验密码
            # 4 签发token
            refresh = TokenObtainPairSerializer.get_token(user)
            data = {'code': 100,
                    'msg': '登录成功',
                    'token': str(refresh),
                    'access': str(refresh.access_token),
                    }
            return data
        else:
            raise ValidationError('用户名或密码错误')

所有接口加权限分类

要求:

        所有接口(除登录外),必须登录后才能访问
        管理员登陆后可以增,删,单查,群查,改 车型,车厂,经销商(具备所有接口权限)
        普通用户登陆可以查看车型,车厂,经销商单条,所有(只有查看权限)

思路:

        1、写五个接口最快捷的方式便是继承 ModelViewSet

        2、关于登录认证这里全局配置是最好的

        3、关于权限认证,有默认权限 IsAuthenticated  和自定义权限 SuperPermission

        4、这里分为普通用户权限和管理员权限,分两大类

        5、给管理员单独写个自定义权限类,在视图层相应配置

# 全局配置登录认证
REST_FRAMEWORK = {    
    'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework_simplejwt.authentication.JWTAuthentication'],  # 认证组件
}

序列化类:

        1、车型表有一对多车厂关系和多对多经销商关系

        2、在模型model中返回自身+车厂信息+经销商信息

        3、返回所以信息,分好序列化和反序列化

    @property
    def car_factory_detail(self):
        return {'name': self.carfactory.name, 'addr': self.carfactory.addr}

    def distributor_detail(self):
        l = []
        for item in self.distributor.all():
            l.append({'name': item.name, 'addr': item.addr, 'telephone': item.telephone})
        return l
# serliazer.py
# 车型:CarModel
from .models import CarModel,CarFactory,Distributor

class CarModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = CarModel
        fields = ['id', 'name', 'price', 'carfactory', 'distributor', 'car_factory_detail', 'distributor_detail']
        extra_kwargs = {
            'car_factory': {'write_only': True},
            'distributor': {'write_only': True},
        }

    car_factory_detail = serializers.DictField(read_only=True)
    distributor_detail = serializers.ListField(read_only=True)


# 车厂:CarFactory
class CarFactorySerializer(serializers.ModelSerializer):
    class Meta:
        model = CarFactory
        fields = '__all__'


# 经销商:Distributor
class DistributorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Distributor
        fields = '__all__'
# views.py
from rest_framework.permissions import IsAuthenticated
from .permission import SuperPermission
from .pagination import CommonPageNumberPagination
from .filters import CommonFilter

# 车型:CarModel
class CarModelView(GenericViewSet, ListAPIView, RetrieveModelMixin):
    permission_classes = [IsAuthenticated]  # 用jwt自带得权限类验证登陆
    queryset = CarModel.objects.all()
    serializer_class = CarModelSerializer
    filter_backends = [CommonFilter]  # 自定义的过滤类
    pagination_class = CommonPageNumberPagination  # 分页


class CarModelDetailView(GenericViewSet, CreateAPIView, UpdateModelMixin, CreateModelMixin, DestroyModelMixin):
    permission_classes = [SuperPermission]
    queryset = CarModel.objects.all()
    serializer_class = CarModelSerializer


# 车厂:CarFactory
class CarFactoryView(GenericViewSet, ListAPIView, RetrieveModelMixin):
    permission_classes = [IsAuthenticated]  # 用jwt自带得权限类验证登陆
    queryset = CarFactory.objects.all()
    serializer_class = CarFactorySerializer
    filter_backends = [CommonFilter]  # 自定义的过滤类
    pagination_class = CommonPageNumberPagination  # 分页


class CarFactoryDetailView(GenericViewSet, CreateAPIView, UpdateModelMixin, CreateModelMixin, DestroyModelMixin):
    permission_classes = [SuperPermission]
    queryset = CarFactory.objects.all()
    serializer_class = CarFactorySerializer


# 经销商:Distributor
class DistributorView(GenericViewSet, ListAPIView, RetrieveModelMixin):
    permission_classes = [IsAuthenticated]  # 用jwt自带得权限类验证登陆
    queryset = Distributor.objects.all()
    serializer_class = DistributorSerializer
    filter_backends = [CommonFilter]  # 自定义的过滤类
    pagination_class = CommonPageNumberPagination  # 分页


class DistributorDetailView(GenericViewSet, CreateAPIView, UpdateModelMixin, CreateModelMixin, DestroyModelMixin):
    permission_classes = [SuperPermission]
    queryset = Distributor.objects.all()
    serializer_class = DistributorSerializer
# permission.py
# 给管理员定义的权限类
from rest_framework.permissions import BasePermission


# 只有超级管理员能访问,其他人都不能访问,给需要限制得视图加上即可
class SuperPermission(BasePermission):
    def has_permission(self, request, view):
        # 到这,正常应该登录完了,登录完了 request.user 当前登录用户
        try:
            user_type = request.user.is_superuser
            if user_type == 1:
                return True
            else:
                self.message = '您不是管理员,您没有权限操作'
                return False
        except Exception as exc:
            self.message = '您没有登录,您没有权限操作'
            return False

自定义分页和自定义过滤

自定义类注意:

        1、自定义过滤、分页、排序、权限等等都可以直接套用我们自己的模板来写

        2、自定义相应的需在视图层配上响应应用的地方

                permission_classes = [IsAuthenticated]             # 用jwt自带权限

                permission_classes = [SuperPermission]           # 自定义权限

                filter_backends = [CommonFilter]                       # 自定义的过滤类

                pagination_class = CommonPageNumberPagination      # 分页

# pagination.py
# 自定义分类
from rest_framework.pagination import PageNumberPagination

class CommonPageNumberPagination(PageNumberPagination):
    page_size = 3     # 设置的默认会显示的数量
    page_query_param = 'page'
    page_size_query_param = 'size'  # &size=2
    max_page_size = 10    # 最大显示数量
    # http://127.0.0.1:7000/api/v1/books/?search=34&size=2
# filter.py
# 自定义过滤
# 查询所有车型接口,可以按车型名字精准过滤
from rest_framework.filters import BaseFilterBackend

class CommonFilter(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        # queryset 之前所有的数据 Book.objects.all()
        # request 当次请求request
        if request.query_params.get('name'):
            queryset = queryset.filter(name__contains=request.query_params.get('name'))
        # if request.query_params.get('price'):
        #     queryset = queryset.filter(price=request.query_params.get('price'))
        # if request.query_params.get('price__gt'):
        #     queryset = queryset.filter(price__gt=request.query_params.get('price__gt'))
        return queryset

urls路由层

总路由:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include('app01.urls')),
]

分路由:

# app01\urls.py
from django.urls import path
from app01 import views
from rest_framework.routers import SimpleRouter
from .views import CarModelView, CarFactoryView, DistributorView, CarModelDetailView, CarFactoryDetailView, DistributorDetailView


router = SimpleRouter()

router.register('model', CarModelView, 'model')    # 车型
router.register('models', CarModelDetailView, 'models')
router.register('factory', CarFactoryView, 'factory')   # 车厂
router.register('factorys', CarFactoryDetailView, 'factorys')
router.register('people', DistributorView, 'people')   # 经销商
router.register('peoples', DistributorDetailView, 'peoples')

urlpatterns = [
    path('car/', views.UserView.as_view()),
]
urlpatterns += router.urls

全局异常处理

全局异常处理作用:

        postman中报错信息可以定制返回格式

# settings.py
REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'app01.exceptions.common_exception_handler'
}
# exceptions.py
# 全局异常处理
from rest_framework.views import exception_handler
from rest_framework.response import Response

def common_exception_handler(exc, context):
    res = exception_handler(exc, context)
    if res:  # drf异常
        msg = res.data.get('detail') or res.data or '系统异常,请联系系统管理员'
        return Response({'code': 999, 'msg': msg})
    else:
        return Response({'code': 888, 'msg': '系统异常,请联系系统管理员"%s' % str(exc)})

今日思维导图:

你可能感兴趣的:(数据库,python,笔记,开发语言,django)