luffy后端

1创建Banner数据库

由于此数据库中的时间,删除等可以共用,所以我们可以给他单独写一个表模型,最后使它不要在建数据库的时候给建到数据库里面,此段代码可共用,接下来我们继承BaseModels

复制代码
from django.db import models


class BaseModels(models.Model):
    created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
    updated_time = models.DateTimeField(auto_now=True, verbose_name='最后更新时间')
    is_delete = models.BooleanField(default=False, verbose_name='是否删除')
    is_show = models.BooleanField(default=True, verbose_name='是否上架')
    orders = models.IntegerField(verbose_name='优先级')

    class Meta:
        abstract = True
复制代码
复制代码
from utils.models import BaseModels
from django.db import models

class Banner(BaseModels):
    title = models.CharField(max_length=16, unique=True, verbose_name='名称')
    image = models.ImageField(upload_to='banner', verbose_name='图片')
    link = models.CharField(max_length=64, verbose_name='跳转链接')
    info = models.TextField(verbose_name='详情')

    class Meta:
        db_table = 'luffy_banner'
        verbose_name_plural = '轮播图表'

    def __str__(self):
        return self.title
复制代码

此时我们Banner表就建好了,迁移表

在迁移前要注册app
python manage.py makemigrations python manage.py migrate

 

2写Banner接口

通过继承GenericViewSet,ListModelMixin,我们可以很快的将接口写出来,但是如果我们要返回指定格式的数据的话,我们必须从写ListModelMixin中的list方法,把list的数据传给另一个变量,这样我们方便按函数的指定参数传值,由于这个list方法我们也可能多处复用,所以我们又可以给他做一个封装。

from rest_framework.mixins import ListModelMixin
from .response import APIResponse

class BannerListView(ListModelMixin):
    def list(self, request, *args, **kwargs):
        res = super().list(request, *args, **kwargs)
        return APIResponse(data=res.data)
复制代码
from luffyapi.utils.listviews import BannerListView
from .models import Banner
from .serializer import BannerSerializers
# Create your views here.
from rest_framework.viewsets import GenericViewSet
from django.conf import settings

class BannerView(GenericViewSet,BannerListView):
    queryset=Banner.objects.all().filter(is_delete=False,is_show=True).order_by('orders')[:settings.AREA]
    serializer_class=BannerSerializers
复制代码

 3 编写多方式手机号登录接口,验证手机号是否存在接口

复制代码
from rest_framework.decorators import action
from django.shortcuts import render
from rest_framework.viewsets import ViewSet
from .serializer import UserSerializer,UserCodeSerializer
from utils.response import APIResponse
from .models import UserInfo
from rest_framework.exceptions import APIException
from txy_sms import get_code,txy_sms_phone
from django.core.cache import cache
import re


class UserLoginView(ViewSet):
    @action(methods=['POST'],detail=False)
    def login(self,request):
        res = UserSerializer(data=request.data)
        res.is_valid(raise_exception=True)
        token=res.context.get('token')
        icon=res.context.get('icon')
        username=res.context.get('username')
        return APIResponse(token=token,icon=icon,username=username)

    @action(methods=['GET'],detail=False)
    def code(self,request):
        mobile= request.query_params.get('mobile')
        if re.match(r'^1[3-9][0-9]{9}$', mobile):
            code=get_code()
            cache.set('code_%s'%mobile,code)
            res = txy_sms_phone(mobile,code)
            if res:
                return APIResponse(msg='验证码发送成功')
            else:
                return APIResponse(msg='验证码发送失败',code=101)
        else:
            return APIResponse(code=102,msg='手机号不合法')


    @action(methods=['POST'],detail=False)
    def login_code(self,request):
        res = UserCodeSerializer(data=request.data)
        res.is_valid(raise_exception=True)
        token=res.context.get('token')
        icon=res.context.get('icon')
        username=res.context.get('username')
        return APIResponse(token=token,icon=icon,username=username)


class UserView(ViewSet):
    @action(methods=['GET'],detail=False)
    def mobile(self,request):
        try:
            # print(request.query_params.get('mobile'))
            mobile=request.query_params.get('mobile')
            UserInfo.objects.get(mobile=mobile)
            return APIResponse(msg='手机号已存在')
        except Exception as e:
            raise APIException('手机号不存在')
复制代码
复制代码
from rest_framework import serializers
from django.contrib.auth import authenticate
from rest_framework.exceptions import APIException

from rest_framework.exceptions import ValidationError
from django.contrib.auth.hashers import make_password
from django.core.cache import cache
from .models import UserInfo
import re
from utils.get_jwt import get_jwt


class UserSerializer(serializers.ModelSerializer):
    username = serializers.CharField()

    class Meta:
        model = UserInfo
        fields = ['username', 'password']

    def _get_user(self, request):

        username = request.get('username')
        password = request.get('password')
        if re.match(r'^1[3-9][0-9]{9}$', username):
            print(request.get('username'))
            print(request.get('password'))
            # user = UserInfo.objects.all().filter(mobile=username).first()
            user = UserInfo.objects.all().filter(mobile=username).first()
            user = authenticate(username=user.username, password=password)
        elif re.match(r'^.+@.+$', username):
            user = UserInfo.objects.all().filter(email=username).first()
            user = authenticate(username=user.username, password=password)

        else:
            user = authenticate(username=username, password=password)

        if user:
            return user
        else:
            raise ValidationError('用户名或密码错误')

    def validate(self, request):
        user = self._get_user(request)

        token = get_jwt(user)
        self.context['token'] = token
        self.context['icon'] = 'http://127.0.0.1:8000/media/' + str(user.icon)
        self.context['username'] = user.username
        return request


class UserCodeSerializer(serializers.ModelSerializer):
    mobile = serializers.CharField()
    code = serializers.CharField()

    class Meta:
        model = UserInfo
        fields = ['mobile', 'code']

    def _get_user(self, request):

        # print(code)

        mobile = request.get('mobile')
        code = request.get('code')

        if re.match(r'^1[3-9][0-9]{9}$', mobile):
            # user = UserInfo.objects.all().filter(mobile=username).first()
            user = UserInfo.objects.all().filter(mobile=mobile).first()
        else:
            raise APIException('手机号错误')
        if user and code == cache.get('code_%s' % mobile):
            cache.set('code_%s' % mobile, '')
            return user
        else:
            raise ValidationError('验证码错误')

    def validate(self, request):
        user = self._get_user(request)

        token = get_jwt(user)
        self.context['token'] = token
        self.context['icon'] = 'http://127.0.0.1:8000/media/' + str(user.icon)
        self.context['username'] = user.username
        return request
复制代码
复制代码
from rest_framework_jwt.settings import api_settings
from rest_framework.exceptions import ValidationError

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER


def get_jwt(user):
    try:
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        return token
    except Exception as e:
        raise ValidationError(str(e))
复制代码

 4 注册接口

 @action(methods=['POST'], detail=False)
    def register(self, request):
        res = RegisterSerializer(data=request.data)
        res.is_valid(raise_exception=True)
        res.save()
        return APIResponse(msg='注册成功')
复制代码
class RegisterSerializer(serializers.ModelSerializer):
    code = serializers.CharField()

    class Meta:
        model = UserInfo
        fields = ['mobile', 'code', 'password']

    def validate(self, attrs):
        code = attrs.get('code')
        mobile=attrs.get('mobile')
        if not(code == cache.get('code_%s' % mobile)):
            raise APIException('验证码错误')
        attrs['username'] = mobile
        attrs.pop('code')
        return attrs

    def create(self, validated_data):
        user = UserInfo.objects.create_user(**validated_data)
        return user
复制代码

 5 课程分类接口

class CourseCategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = CourseCategory
        fields = ['id', 'name']
class CourseCategoryView(GenericViewSet, CommonListModelMixin):
    queryset = CourseCategory.objects.all().filter(is_delete=False, is_show=True).order_by('orders')
    serializer_class = CourseCategorySerializer

5.1 所有课程接口

复制代码
class TeacherSerializer(serializers.ModelSerializer):
    class Meta:
        model = Teacher
        # fields = '__all__'
        fields = ['id','name','role','title','signature','image','brief']

class CourseSerializer(serializers.ModelSerializer):  # 课程列表和课程详情都用这个序列化类,序列化的字段多了好几个
    # 方式三:子序列化:通过老师的序列化类来实现序列化
    teacher=TeacherSerializer()
    class Meta:
        model = Course
        # fields = ['id', 'name'] # 要序列化更多字段
        fields = [
            'id',
            'name',
            'course_img',
            'price',  # 课程价格
            'students',  # 学生数量
            'pub_sections',  # 发布了多少课时
            'sections',  # 总课时数量

            'brief',  # 课程介绍
            'attachment_path',  # 课程课件地址
            'period',  # 建议学习周期

            'course_type_name',  # 课程类型名字,表中没有这个字段,表模型中重写方法
            'level_name',  # 级别名字
            'status_name',  # 状态名字

            # 'teacher_detail',  # 老师  {name:xx,title:xxx}
            'teacher',
            'section_list', # 课时 最多4个课时 [{},{},{},{}]
        ]  # 要序列化更多字段:图片,有多少课时,更新了多少,学生数量,价格。。。   teacher字段:{name:xx,title:asdf}   章节下的课时,最多四个课时

    # 指定序列化的字段:三种方式
    # 方式一:在表模型中写

    # 方式二:序列化类中写
    # teacher = serializers.SerializerMethodField()
    # def get_teacher(self, obj):
    #     return {'name': obj.teacher.name, 'title': obj.teacher.title, 'image': str(obj.teacher.image)}

    # 方式三:子序列化:通过老师的序列化类来实现序列化
复制代码
class CourseView(GenericViewSet, CommonListModelMixin,RetrieveModelMixin):
    queryset = Course.objects.all().filter(is_delete=False, is_show=True).order_by('orders')
    serializer_class = CourseSerializer
    pagination_class =PageNumberPagination
    filter_backends=[OrderingFilter,DjangoFilterBackend]
    ordering_fields=['orders','price','students']
    # 加过滤:按课程分类过滤-- django-filter实现
    filterset_fields=['course_category']
复制代码
class Course(BaseModel):
    """课程"""
    course_type = (
        (0, '付费'),
        (1, 'VIP专享'),
        (2, '学位课程')
    )
    level_choices = (
        (0, '初级'),
        (1, '中级'),
        (2, '高级'),
    )
    status_choices = (
        (0, '上线'),
        (1, '下线'),
        (2, '预上线'),
    )
    name = models.CharField(max_length=128, verbose_name="课程名称")
    # blank 后台管理录入可以为空,null存到数据库字段可以为空
    course_img = models.ImageField(upload_to="courses", max_length=255, verbose_name="封面图片", blank=True, null=True)
    # 付费类型
    course_type = models.SmallIntegerField(choices=course_type, default=0, verbose_name="付费类型")
    # TextField 大文本, 存html
    brief = models.TextField(max_length=2048, verbose_name="详情介绍", null=True, blank=True)
    # 难度等级
    level = models.SmallIntegerField(choices=level_choices, default=0, verbose_name="难度等级")
    # 发布日期
    pub_date = models.DateField(verbose_name="发布日期", auto_now_add=True)
    # 建议学习周期
    period = models.IntegerField(verbose_name="建议学习周期(day)", default=7)
    # 课件路径
    attachment_path = models.FileField(upload_to="attachment", max_length=128, verbose_name="课件路径", blank=True,
                                       null=True)
    status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="课程状态")
    # 优化字段
    students = models.IntegerField(verbose_name="学习人数", default=0)
    # 课程一边录,一边传
    sections = models.IntegerField(verbose_name="总课时数量", default=0)
    pub_sections = models.IntegerField(verbose_name="课时更新数量", default=0)
    price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="课程原价", default=0)

    # 关联字段
    # ForeignKey的on_delete的选择:
    # models.CASCADE    级联删除,用的少,除非真的要删除
    # models.SET_NULL   关联字段设为空   null=True
    # models.SET_DEFAULT 关联字段设为默认值 defalut='asfasd'
    # models.DO_NOTHING  什么都不做, 不用强外键关联
    # models.SET()       放一个函数内存地址,关联字段删除时,执行这个函数

    # 外键关联的好处和坏处
    #     -好处在于 插入修改数据,有校验,能够保证数据不会错乱,不会出现脏数据
    #     -坏处在于 有校验,速度慢,数量越大,越慢,咱们可以通过程序控制不加入脏数据
    # 公司内部为了效率,一般不建立外键关联,关系在 ,只是没有那条线了

    # 在django中不建立外键关联,只是不创建外键,关联关系还在【关联查询】,也是使用ForeignKey,只是加一个参数,加了之后,没有约束,但你们关系还在
    teacher = models.ForeignKey("Teacher", on_delete=models.DO_NOTHING, null=True, blank=True, verbose_name="授课老师",
                                db_constraint=False)
    course_category = models.ForeignKey("CourseCategory", on_delete=models.SET_NULL, db_constraint=False, null=True,
                                        blank=True, verbose_name="课程分类")

    class Meta:
        db_table = "luffy_course"
        verbose_name = "课程"
        verbose_name_plural = "课程"

    def __str__(self):
        return "%s" % self.name

    # 序列化的字段方法
    def course_type_name(self):
        return self.get_course_type_display()

    def level_name(self):
        return self.get_level_display()

    def status_name(self):
        return self.get_status_display()

    # 序列化teacher的方法
    # def teacher_detail(self):
    #     return {'name': self.teacher.name}

    def section_list(self):
        section_list = []
        # 先根据课程取出所有章节,循环每个章节,取出所有课时,拼接到列表中,当列表长度等于四,就结束
        # 取到所有章节   原来是   表名小写_set.all()
        course_chapter_list = self.coursechapters.all()
        for chapter in course_chapter_list:
            # 拿到该章节下所有课时   原来是   表名小写_set.all()
            course_section_list = chapter.coursesections.all()
            for section in course_section_list:
                section_list.append({'id': section.id,
                                     'name': section.name,
                                     'section_link': section.section_link,
                                     'duration': section.duration,  # 视频时长
                                     'free_trail': section.free_trail,
                                     })
                if len(section_list) >= 4:
                    return section_list

        return section_list
复制代码
class CommonPageNumberPagination(PageNumberPagination):
    page_size = 2
    page_query_param = 'page'
    page_size_query_param = 'size'
    max_page_size = 5

5.2 课程详情页接口

class CourseChapterView(GenericViewSet, CommonListModelMixin):
    queryset = CourseChapter.objects.all().filter(is_delete=False, is_show=True).order_by('orders')
    serializer_class = CourseChapterSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['course']
复制代码
class CourseSectionSerializer(serializers.ModelSerializer):
    class Meta:
        model = CourseSection
        fields = '__all__'


class CourseChapterSerializer(serializers.ModelSerializer):
    # 三种:序列化类中写,表模型中写,子序列化
    # 一个章节下,有很多课时,课时是多条,子序列化的时候,一定不要忘了many=True
    coursesections = CourseSectionSerializer(many=True)

    class Meta:
        model = CourseChapter
        fields = ['id', 'chapter', 'name', 'summary', 'coursesections']
复制代码

6 搜索后台接口

复制代码
# es:分布式的全文检索引擎,实现像百度搜索的样子
class CourseSearchView(GenericViewSet, ListModelMixin):
    queryset = Course.objects.all().filter(is_delete=False, is_show=True).order_by('orders')
    serializer_class = CourseSerializer
    pagination_class =PageNumberPagination
    filter_backends=[SearchFilter]
    # 加过滤:按课程分类过滤-- django-filter实现
    search_fields=['name']
    # 目前只能搜实战课 :轻课,实战课,免费课

    # def list(self, request, *args, **kwargs):
    #     res=super().list( request, *args, **kwargs)
    #     # res.data   是搜出来的实战课课程
    #
    #     #搜轻课
    #     words=request.query_params.get('search')
    #     # 去轻课表搜
    #     # 去免费课表搜
    #
    #     #{code:100,msg:成功,actual_list:[{},{},{}],free_list:[{},{},{}],light_list:[{},{},{}]}
    #
    #     return APIResponse(code=100,msg='成功',actual_list=res.data,free_list='',light_list='')
复制代码

7 生成订单接口

复制代码
from django.shortcuts import render

# Create your views here.
from .models import Order
# 自动生成路由,新增功能:订单表存数据
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import CreateModelMixin
from .serializer import OrderSerializer
from utils.response import APIResponse

from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated
class OrderView(GenericViewSet):
    queryset = Order.objects.all()
    serializer_class = OrderSerializer
    # 登录后才能访问
    authentication_classes = [JSONWebTokenAuthentication]
    permission_classes = [IsAuthenticated]

    # 这个新增接口,既要新增,又要返回支付链接,冲写create方法
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data,context={'request':request})
        serializer.is_valid(raise_exception=True)
        serializer.save()
        pay_url = serializer.context.get('pay_url')
        return APIResponse(pay_url=pay_url)
复制代码
复制代码
from rest_framework import serializers
from .models import Order, OrderDetail
from course.models import Course
from rest_framework.exceptions import APIException
from django.conf import settings


class OrderSerializer(serializers.ModelSerializer):
    # courses不是order表的字段,一定要重写
    # 前端传入的是 courses:[1,3,4]----->转换成课程对象[课程对象1,课程对象3,课程对象4]
    courses = serializers.PrimaryKeyRelatedField(queryset=Course.objects.all(), write_only=True, many=True)

    class Meta:
        model = Order
        fields = ['courses', 'total_amount', 'subject', 'pay_type']  # 只用来做数据校验和反序列化

    def _check_price(self, attrs):
        total_amount = attrs.get('total_amount')
        # 循环所有课程,取出价格累加得到总价格
        real_total = 0
        for course in attrs.get('courses'):
            real_total += course.price
        if not total_amount == real_total:  # 不正常,抛异常
            raise APIException('价格不一致')
        else:
            return real_total

    def _get_trade_no(self):
        import uuid
        return str(uuid.uuid4())

    def _get_user(self):
        # 当前登录用户,request.user
        request = self.context.get('request')
        return request.user  # 必须通过了登录认证,才有当前登录用户

    def _get_pay_url(self, total_amount, subject, trade_no):
        from libs.alipay_common import GATEWAY, alipay
        pay_str = alipay.api_alipay_trade_page_pay(
            out_trade_no=trade_no,
            total_amount=float(total_amount),
            subject=subject,
            return_url=settings.RETURN_URL,  # get回调地址
            notify_url=settings.NOTIFY_URL  # post回调地址
        )
        return GATEWAY + pay_str

    def _before_create(self, pay_url, attrs, user, trade_no):
        self.context['pay_url'] = pay_url
        attrs['user'] = user
        attrs['out_trade_no'] = trade_no
        # 要不要courses剔除? attrs:{courses:[对象1,对象2],total_amount:11,subject:xx,pay_type:1,user:user,out_trade_no:3333}

    def validate(self, attrs):
        # 1 校验价格:计算一下总价格和后端算出来的总价格是否一致
        total_amount = self._check_price(attrs)
        # 2)生成订单号:唯一的,
        trade_no = self._get_trade_no()
        # 3)支付用户:request.user
        user = self._get_user()
        # 4)支付链接生成,放入到self.context中
        pay_url = self._get_pay_url(total_amount, attrs.get('subject'), trade_no)
        # 5)入库(两个表)的信息准备:重写create方法
        self._before_create(pay_url, attrs, user, trade_no)

        return attrs

    def create(self, validated_data):
        # {courses:[对象1,对象2],total_amount:11,subject:xx,pay_type:1,user:user,out_trade_no:3333}
        courses = validated_data.pop('courses')
        order = Order.objects.create(**validated_data)
        # 存订单详情表
        for course in courses:
            OrderDetail.objects.create(order=order, course=course, price=course.price, real_price=course.price)

        return order
复制代码

 

posted @   shangxin_bai  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示