luffy后台
""" ├── luffyapi ├── logs/ # 项目运行时/开发时日志目录 - 包 ├── manage.py # 脚本文件 ├── luffyapi/ # 项目主应用,开发时的代码保存 - 包 ├── apps/ # 开发者的代码保存目录,以模块[子应用]为目录保存 - 包 ├── libs/ # 第三方类库的保存目录[第三方组件、模块] - 包 ├── settings/ # 配置目录 - 包 ├── dev.py # 项目开发时的本地配置 └── prod.py # 项目上线时的运行配置 ├── urls.py # 总路由 └── utils/ # 多个模块[子应用]的公共函数类库[自己开发的组件] └── scripts/ # 保存项目运营时的脚本文件 - 文件夹 """
from celery import Celery # 加载django环境 import os import django os.environ.setdefault("DJANGO_SETTINGS_MODULE", "luffyapi.settings.dev") django.setup() broker='redis://127.0.0.1:6379/1' #broker任务队列 backend='redis://127.0.0.1:6379/2' # 结构存储,执行完的结果存在这 app=Celery(__name__,broker=broker,backend=backend,include=['celery_task.home_task',]) # 执行定时任务 # 时区 app.conf.timezone = 'Asia/Shanghai' # 是否使用UTC app.conf.enable_utc = False # 任务的定时配置 from datetime import timedelta from celery.schedules import crontab app.conf.beat_schedule = { 'add-task': { 'task': 'celery_task.home_task.banner_update', 'schedule': timedelta(seconds=30), # 'schedule': crontab(hour=8, day_of_week=1), # 每周一早八点 # 'args': (300, 150), } } # 一定要启动beat # celery beat -A celery_task -l info
from .celery import app # cache # model,serilizer @app.task def banner_update(): from home import serializer from home import models from django.conf import settings from django.core.cache import cache queryset_banner = models.Banner.objects.filter(is_delete=False, is_show=True).order_by('orders')[ :settings.BANNER_COUNTER] serializer_banner=serializer.BannerModelSerilaizer(instance=queryset_banner,many=True) # print(serializer_banner.data) for banner in serializer_banner.data: banner['img']='http://127.0.0.1:8000'+banner['img'] cache.set('banner_list',serializer_banner.data) # import time # time.sleep(1) # banner_list=cache.get('banner_list') # print(banner_list) return True
.......
import xadmin from . import models xadmin.site.register(models.CourseCategory) xadmin.site.register(models.Course) xadmin.site.register(models.Teacher) xadmin.site.register(models.CourseChapter) xadmin.site.register(models.CourseSection)
from django.apps import AppConfig class CourseConfig(AppConfig): name = 'course'
# 自定义过滤规则 from rest_framework.filters import BaseFilterBackend class MyFilter(BaseFilterBackend): def filter_queryset(self, request, queryset, view): # 真正的过滤规则 # params=request.GET.get('teacher') # queryset.filter('''''') return queryset[:1] from django_filters.filterset import FilterSet from django_filters import filters from . import models class CourseFilterSet(FilterSet): # 课程的价格范围要大于min_price,小于max_price min_price = filters.NumberFilter(field_name='price', lookup_expr='gt') max_price = filters.NumberFilter(field_name='price', lookup_expr='lt') class Meta: model=models.Course fields=['course_category']
from django.db import models # Create your models here. # 分析过程 # class Course(models.Model): # name = models.CharField(max_length=64) # title = models.CharField(max_length=64) # # level = models.IntegerField(choices=((0, '入门'), (1, '进阶')), default=0) # # detail = models.TextField() # 可以关联详情表 # type = models.IntegerField(choices=((0, 'Python'), (1, 'Linux')), default=0) # is_show = models.BooleanField(default=False) # # # 优化字段 # # 总学生 # students = models.IntegerField(default=0) # # 总学时 # time = models.IntegerField(default=0) # # class Meta: # abstract = True # # # 免费课 # class FreeCourse(Course): # image = models.ImageField(upload_to='course/free') # attachment = models.FileField(upload_to='attachment') # # # 实战课 # class ActualCourse(Course): # image = models.ImageField(upload_to='course/actual') # price = models.DecimalField(max_digits=7, decimal_places=2) # # cost = models.DecimalField(max_digits=7, decimal_places=2) # # # 轻课 # class LightCourse(Course): # image = models.ImageField(upload_to='course/light') # price = models.DecimalField(max_digits=7, decimal_places=2) # # cost = models.DecimalField(max_digits=7, decimal_places=2) # period = models.IntegerField(verbose_name='学习建议周期(month)', default=0) from luffyapi.utils.models import BaseModel # 实际路飞课程相关表,以免费课为例 class CourseCategory(BaseModel): """分类 python,linux,go, 网络安全 跟课程是一对多的关系 """ name = models.CharField(max_length=64, unique=True, verbose_name="分类名称") class Meta: db_table = "luffy_course_category" verbose_name = "分类" verbose_name_plural = verbose_name def __str__(self): return "%s" % self.name 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="课程名称") 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="付费类型") # 使用这个字段的原因 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="课程状态") price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="课程原价", default=0) # 优化字段 students = models.IntegerField(verbose_name="学习人数", default=0) sections = models.IntegerField(verbose_name="总课时数量", default=0) pub_sections = models.IntegerField(verbose_name="课时更新数量", default=0) # 关联字段 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 @property def course_type_name(self): return self.get_course_type_display() @property def level_name(self): return self.get_level_display() @property def status_name(self): return self.get_status_display() @property def section_list(self): ll=[] # 根据课程取出所有章节(正向查询,字段名.all()) course_chapter_list=self.coursechapters.all() for course_chapter in course_chapter_list: # 通过章节对象,取到章节下所有的课时(反向查询) # course_chapter.表名小写_set.all() 现在变成了course_chapter.coursesections.all() course_sections_list=course_chapter.coursesections.all() for course_section in course_sections_list: ll.append({ 'name': course_section.name, 'section_link': course_section.section_link, 'duration': course_section.duration, 'free_trail': course_section.free_trail, }) if len(ll)>=4: return ll return ll class Teacher(BaseModel): """导师 跟课程一对多,关联字段写在课程表中 """ role_choices = ( (0, '讲师'), (1, '导师'), (2, '班主任'), ) name = models.CharField(max_length=32, verbose_name="导师名") role = models.SmallIntegerField(choices=role_choices, default=0, verbose_name="导师身份") title = models.CharField(max_length=64, verbose_name="职位、职称") signature = models.CharField(max_length=255, verbose_name="导师签名", help_text="导师签名", blank=True, null=True) image = models.ImageField(upload_to="teacher", null=True, verbose_name="导师封面") brief = models.TextField(max_length=1024, verbose_name="导师描述") class Meta: db_table = "luffy_teacher" verbose_name = "导师" verbose_name_plural = verbose_name def __str__(self): return "%s" % self.name def role_name(self): # 返回角色的中文 return self.get_role_display() class CourseChapter(BaseModel): """章节 章节跟课程是一(课程)对多(章节多) """ course = models.ForeignKey("Course", related_name='coursechapters', on_delete=models.CASCADE, verbose_name="课程名称",db_constraint=False) chapter = models.SmallIntegerField(verbose_name="第几章", default=1) name = models.CharField(max_length=128, verbose_name="章节标题") summary = models.TextField(verbose_name="章节介绍", blank=True, null=True) pub_date = models.DateField(verbose_name="发布日期", auto_now_add=True) class Meta: db_table = "luffy_course_chapter" verbose_name = "章节" verbose_name_plural = verbose_name def __str__(self): return "%s:(第%s章)%s" % (self.course, self.chapter, self.name) class CourseSection(BaseModel): """课时 章节和课时是一对多的关系,关联字段写在多的一方,课时 """ section_type_choices = ( (0, '文档'), (1, '练习'), (2, '视频') ) chapter = models.ForeignKey("CourseChapter", related_name='coursesections', on_delete=models.CASCADE, verbose_name="课程章节",db_constraint=False) name = models.CharField(max_length=128, verbose_name="课时标题") orders = models.PositiveSmallIntegerField(verbose_name="课时排序") section_type = models.SmallIntegerField(default=2, choices=section_type_choices, verbose_name="课时种类") section_link = models.CharField(max_length=255, blank=True, null=True, verbose_name="课时链接", help_text="若是video,填vid,若是文档,填link") duration = models.CharField(verbose_name="视频时长", blank=True, null=True, max_length=32) # 仅在前端展示使用 pub_date = models.DateTimeField(verbose_name="发布时间", auto_now_add=True) free_trail = models.BooleanField(verbose_name="是否可试看", default=False) class Meta: db_table = "luffy_course_Section" verbose_name = "课时" verbose_name_plural = verbose_name def __str__(self): return "%s-%s" % (self.chapter, self.name) @property def section_type_name(self): return self.get_section_type_display()
from rest_framework.pagination import PageNumberPagination as DRFPageNumberPagination class PageNumberPagination(DRFPageNumberPagination): page_size=1 page_query_param = 'page' max_page_size = 10 page_size_query_param='page_size'
from rest_framework import serializers from . import models class CourseCategorySerializer(serializers.ModelSerializer): class Meta: model = models.CourseCategory fields = ['id', 'name'] class TeacherSerializer(serializers.ModelSerializer): class Meta: model = models.Teacher fields = ('name', 'role_name', 'title', 'signature', 'image', 'brief') class CourseModelSerializer(serializers.ModelSerializer): # 子序列化的方式 teacher = TeacherSerializer() class Meta: model = models.Course fields=[ 'id', 'name', 'course_img', 'brief', 'attachment_path', 'pub_sections', 'price', 'students', 'period', 'sections', 'course_type_name', 'level_name', 'status_name', 'teacher', 'section_list', ] # fields = ['id', 'name', # 'course_img', # 'brief', # 'teacher', # 'course_type_name', # 'status_name', # 'level_name', # 'course_sections' # ] class CourseSectionSerializer(serializers.ModelSerializer): class Meta: model=models.CourseSection fields=['name','orders','duration','free_trail','section_link','section_type_name'] class CourseChapterSerializer(serializers.ModelSerializer): # 子序列化的方式 coursesections=CourseSectionSerializer(many=True) class Meta: model=models.CourseChapter fields=['name','summary','chapter','coursesections']
from django.urls import path, include from . import views from rest_framework.routers import SimpleRouter router = SimpleRouter() router.register('categories', views.CourseCategoryView, 'categories') router.register('free', views.CouresView, 'free') router.register('chapters', views.CourseChapterView, 'coursechapter') router.register('search', views.CouresSearchView, 'search') urlpatterns = [ path('', include(router.urls)), ]
from django.shortcuts import render # Create your views here. from rest_framework.viewsets import GenericViewSet from rest_framework.mixins import ListModelMixin,RetrieveModelMixin from . import models from . import serializer class CourseCategoryView(GenericViewSet,ListModelMixin): queryset = models.CourseCategory.objects.filter(is_delete=False,is_show=True).order_by('orders') serializer_class = serializer.CourseCategorySerializer from .paginations import PageNumberPagination from django_filters.rest_framework import DjangoFilterBackend from rest_framework.filters import OrderingFilter,SearchFilter from .filters import MyFilter,CourseFilterSet class CouresView(GenericViewSet,ListModelMixin,RetrieveModelMixin): queryset = models.Course.objects.filter(is_delete=False,is_show=True).order_by('orders') serializer_class = serializer.CourseModelSerializer pagination_class = PageNumberPagination # 过滤和排序 # filter_backends=[DjangoFilterBackend,OrderingFilter,SearchFilter] # filter_backends=[DjangoFilterBackend,OrderingFilter,MyFilter] filter_backends=[DjangoFilterBackend,OrderingFilter] # # filter_backends=OrderingFilter ordering_fields=['id', 'price', 'students'] # search_fields=['name'] # filter_fields=['course_category'] # # g同过django-filter扩展过滤 # filter_backends = [DjangoFilterBackend] # # 原来是配置字段,现在配置类 filter_class = CourseFilterSet ''' django-filters指定以某个字段过滤有两种方式 第一种: 配置类: filter_backends=[DjangoFilterBackend] 配置字段: filter_fields=['course_category'] 第二种: 配置类: filter_backends=[DjangoFilterBackend] 配置类:(自己写的类) class CourseFilterSet(FilterSet): class Meta: model=models.Course fields=['course_category'] filter_class = CourseFilterSet 第三种:实现区间过滤 class CourseFilterSet(FilterSet): # 课程的价格范围要大于min_price,小于max_price min_price = filters.NumberFilter(field_name='price', lookup_expr='gt') max_price = filters.NumberFilter(field_name='price', lookup_expr='lt') class Meta: model=models.Course fields=['course_category'] 配置类: filter_backends=[DjangoFilterBackend] 配置类:(自己写的类) -filter_class = CourseFilterSet ''' ''' 排序: 按id正序倒叙排序,按price正序倒叙排列 使用:http://127.0.0.1:8000/course/free/?ordering=-id 配置类: filter_backends=[OrderingFilter] 配置字段: ordering_fields=['id','price'] 内置过滤: 使用:http://127.0.0.1:8000/course/free/?search=39 按照price过滤(表自有的字段直接过滤) 配置类: filter_backends=[SearchFilter] 配置字段: search_fields=['price'] 扩展:django-filter 安装: 支持自由字段的过滤还支持外键字段的过滤 http://127.0.0.1:8000/course/free/?course_category=1 # 过滤分类为1 (python的所有课程) 配置类: filter_backends=[DjangoFilterBackend] 配置字段: filter_fields=['course_category'] ''' #如何自定义排序或者过滤类 # 课程章节接口 class CourseChapterView(GenericViewSet,ListModelMixin): queryset = models.CourseChapter.objects.filter(is_delete=False,is_show=True) serializer_class = serializer.CourseChapterSerializer # 可以按照课程id来查 filter_backends = [DjangoFilterBackend] filter_fields = ['course'] # 搜索接口 class CouresSearchView(GenericViewSet,ListModelMixin): queryset = models.Course.objects.filter(is_delete=False,is_show=True) serializer_class = serializer.CourseModelSerializer pagination_class = PageNumberPagination filter_backends=[SearchFilter] search_fields=['name']
.......
import xadmin # 注册banner表 from . import models xadmin.site.register(models.Banner)
from django.db import models # from luffyapi.utils.models import BaseModel from utils.models import BaseModel class Banner(BaseModel): name = models.CharField(max_length=32, verbose_name='图片名字') img = models.ImageField(upload_to='banner', verbose_name='轮播图', help_text='图片尺寸必须是:3840*800', null=True) link = models.CharField(max_length=32, verbose_name='跳转连接') info = models.TextField(verbose_name='图片简介') # type=models.IntegerField(choices=) def __str__(self): return self.name
from rest_framework import serializers from . import models class BannerModelSerilaizer(serializers.ModelSerializer): class Meta: model = models.Banner fields = ['name', 'link', 'img']
from django.urls import path, re_path, include from . import views from rest_framework.routers import SimpleRouter router = SimpleRouter() router.register('banner', views.BannerView, 'banner') urlpatterns = [ # path('banner/', views.BannerView.as_view()), path('', include(router.urls)), ]
from django.shortcuts import render from rest_framework.views import APIView from luffyapi.utils.response import APIResponse from luffyapi.utils.logger import log from rest_framework.mixins import ListModelMixin from rest_framework.generics import GenericAPIView from rest_framework.viewsets import GenericViewSet from . import models from . import serializer # class BannerView(GenericAPIView,ListModelMixin): # 这个路由配置 path('banner/', views.BannerView.as_view()), # 路由位置 ''' from rest_framework.routers import SimpleRouter router=SimpleRouter() router.register('banner',views.BannerView,'banner') path('', include(router.urls)), ''' from django.conf import settings from django.core.cache import cache from rest_framework.response import Response class BannerView(GenericViewSet, ListModelMixin): # 无论有多少条待展示的数据,最多就展示3条 queryset = models.Banner.objects.filter(is_delete=False, is_show=True).order_by('orders')[ :settings.BANNER_COUNTER] serializer_class = serializer.BannerModelSerilaizer def list(self, request, *args, **kwargs): # response=super().list(request, *args, **kwargs) # 把data的数据加缓存 # 1 先去缓存拿数据 banner_list=cache.get('banner_list') if not banner_list: print('走数据库了') # 缓存中没有,去数据库拿 response =super().list(request, *args, **kwargs) # 加到缓存 cache.set('banner_list',response.data,60*60*24) return response return Response(data=banner_list)
.......
from django.db import models # Create your models here. from django.db import models from user.models import User from course.models import Course class Order(models.Model): """订单模型""" status_choices = ( (0, '未支付'), (1, '已支付'), (2, '已取消'), (3, '超时取消'), ) pay_choices = ( (1, '支付宝'), (2, '微信支付'), ) subject = models.CharField(max_length=150, verbose_name="订单标题") total_amount = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="订单总价", default=0) out_trade_no = models.CharField(max_length=64, verbose_name="订单号", unique=True) trade_no = models.CharField(max_length=64, null=True, verbose_name="流水号") # 支付宝生成回来的 order_status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="订单状态") pay_type = models.SmallIntegerField(choices=pay_choices, default=1, verbose_name="支付方式") pay_time = models.DateTimeField(null=True, verbose_name="支付时间") # 一个用户可以下多个订单,一个订单只属于一个用户,一对多的关系,关联字段写在多个一方,写在order方 user = models.ForeignKey(User, related_name='order_user', on_delete=models.DO_NOTHING, db_constraint=False, verbose_name="下单用户") created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') updated_time = models.DateTimeField(auto_now=True, verbose_name='最后更新时间') class Meta: db_table = "luffy_order" verbose_name = "订单记录" verbose_name_plural = "订单记录" def __str__(self): return "%s - ¥%s" % (self.subject, self.total_amount) @property def courses(self): data_list = [] for item in self.order_courses.all(): data_list.append({ "id": item.id, "course_name": item.course.name, "real_price": item.real_price, }) return data_list # 订单和详情是一对多,关联字段写在多个的一方,写在订单详情表中 class OrderDetail(models.Model): """订单详情""" order = models.ForeignKey(Order, related_name='order_courses', on_delete=models.CASCADE, db_constraint=False, verbose_name="订单") course = models.ForeignKey(Course, related_name='course_orders', on_delete=models.SET_NULL, db_constraint=False, verbose_name="课程",null=True) price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="课程原价") real_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="课程实价") class Meta: db_table = "luffy_order_detail" verbose_name = "订单详情" verbose_name_plural = "订单详情" def __str__(self): try: return "%s的订单:%s" % (self.course.name, self.order.out_trade_no) except: return super().__str__()
from rest_framework import serializers from . import models from rest_framework.exceptions import ValidationError from course.models import Course from django.conf import settings class OrderSerializer(serializers.ModelSerializer): # 前端传什么数据过来{course:[1,2,3],total_amount:100,subject:xx商品,pay_type:1,} # user字段需要,但是不是传的,使用了jwt # 需要把course:[1,2,3] 处理成 course:[obj1,obj2,obj3] # 课时:[1,4,6,]===>课时:[obj1,obj4,obj6,] # course=serializers.CharField() course=serializers.PrimaryKeyRelatedField(queryset=Course.objects.all(), write_only=True, many=True) class Meta: model = models.Order fields = ['total_amount','subject','pay_type','course'] extra_kwargs={ 'total_amount':{'required':True}, 'pay_type': {'required': True}, } def _check_price(self,attrs): total_amount=attrs.get('total_amount') course_list=attrs.get('course') total_price=0 for course in course_list: total_price+=course.price if total_price!=total_amount: raise ValidationError('价格不合法') return total_amount def _gen_out_trade_no(self): import uuid return str(uuid.uuid4()).replace('-','') def _get_user(self): # 需要request对象(需要视图通过context把reuqest对象传入。重写create方法) request=self.context.get('request') return request.user def _gen_pay_url(self,out_trade_no,total_amout,subject): # total_amout是Decimal类型,识别不了,需要转换成float类型 from luffyapi.libs.al_pay import alipay,gateway order_string = alipay.api_alipay_trade_page_pay ( out_trade_no=out_trade_no, total_amount=float(total_amout), subject=subject, return_url=settings.RETURN_URL, # get回调,前台地址 notify_url=settings.NOTIFY_URL # post回调,后台地址 ) return gateway+order_string def _before_create(self,attrs,user,pay_url,out_trade_no): attrs['user']=user attrs['out_trade_no']=out_trade_no self.context['pay_url']=pay_url def validate(self, attrs): ''' # 1)订单总价校验 # 2)生成订单号 # 3)支付用户:request.user # 4)支付链接生成 # 5)入库(两个表)的信息准备 ''' # 1)订单总价校验 total_amout = self._check_price(attrs) # 2)生成订单号 out_trade_no=self._gen_out_trade_no() # 3)支付用户:request.user user=self._get_user() # 4)支付链接生成 pay_url=self._gen_pay_url(out_trade_no,total_amout,attrs.get('subject')) # 5)入库(两个表)的信息准备 self._before_create(attrs,user,pay_url,out_trade_no) return attrs def create(self, validated_data): course_list=validated_data.pop('course') order=models.Order.objects.create(**validated_data) for course in course_list: models.OrderDetail.objects.create(order=order,course=course,price=course.price,real_price=course.price) return order
from django.urls import path, re_path, include from . import views from rest_framework.routers import SimpleRouter router = SimpleRouter() router.register('pay', views.PayView, 'pay') urlpatterns = [ path('', include(router.urls)), path('success/', views.SuccessView.as_view()), ]
from django.shortcuts import render # Create your views here. from rest_framework.viewsets import GenericViewSet from rest_framework.mixins import CreateModelMixin from . import models from . import serializer from rest_framework_jwt.authentication import JSONWebTokenAuthentication from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView class PayView(GenericViewSet,CreateModelMixin): authentication_classes = [JSONWebTokenAuthentication,] permission_classes = [IsAuthenticated,] queryset = models.Order.objects.all() serializer_class = serializer.OrderSerializer # 重写create方法 def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data,context={'request':request}) serializer.is_valid(raise_exception=True) self.perform_create(serializer) return Response(serializer.context.get('pay_url')) class SuccessView(APIView): def get(self,request,*args,**kwargs): out_trade_no=request.query_params.get('out_trade_no') order=models.Order.objects.filter(out_trade_no=out_trade_no).first() if order.order_status==1: return Response(True) else: return Response(False) def post(self,request,*args,**kwargs): ''' 支付宝回调接口 ''' from luffyapi.libs.al_pay import alipay from luffyapi.utils.logger import log # 注意这个小细节,把他转成字典 data = request.data.dict() out_trade_no=data.get('out_trade_no',None) gmt_payment=data.get('gmt_payment',None) signature = data.pop("sign") # 验证签名 success = alipay.verify(data, signature) if success and data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED"): models.Order.objects.filter(out_trade_no=out_trade_no).update(order_status=1,pay_time=gmt_payment) log.info('%s订单支付成功'%out_trade_no) return Response('success') else: log.info('%s订单有问题' % out_trade_no) return Response('error')
.......
import xadmin from xadmin import views class GlobalSettings(object): """xadmin的全局配置""" site_title = "路飞学城" # 设置站点标题 site_footer = "路飞学城有限公司" # 设置站点的页脚 # menu_style = "accordion" # 设置菜单折叠 xadmin.site.register(views.CommAdminView, GlobalSettings) # 默认xadmin就已经把权限6表注册了
from django.db import models from django.contrib.auth.models import AbstractUser class User(AbstractUser): telephone = models.CharField(max_length=11) icon = models.ImageField(upload_to='icon', default='icon/default.png')
from rest_framework import serializers from . import models from rest_framework.exceptions import ValidationError import re from django.core.cache import cache from django.conf import settings class UserSerilaizer(serializers.ModelSerializer): username = serializers.CharField() class Meta: model = models.User fields = ['username', 'password', 'id'] extra_kwargs = { 'id': {'read_only': True}, 'password': {'write_only': True} } def validate(self, attrs): # 多种登录方式 user = self._get_user(attrs) # 签发token token = self._get_token(user) # 放到context中,我在视图类中可以取出来 self.context['token'] = token self.context['user'] = user return attrs def _get_user(self, attrs): username = attrs.get('username') password = attrs.get('password') import re if re.match('^1[3-9][0-9]{9}$', username): user = models.User.objects.filter(telephone=username).first() elif re.match('^.+@.+$', username): # 邮箱 user = models.User.objects.filter(email=username).first() else: user = models.User.objects.filter(username=username).first() if user: ret = user.check_password(password) if ret: return user else: raise ValidationError('密码错误') else: raise ValidationError('用户不存在') def _get_token(self, user): from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler payload = jwt_payload_handler(user) # 通过user对象获得payload token = jwt_encode_handler(payload) # 通过payload获得token return token class CodeUserSerilaizer(serializers.ModelSerializer): code=serializers.CharField() class Meta: model = models.User fields = ['telephone', 'code'] def validate(self, attrs): user=self._get_user(attrs) # 用户存在,签发token token = self._get_token(user) self.context['token'] = token self.context['user'] = user return attrs def _get_user(self, attrs): telephone = attrs.get('telephone') code = attrs.get('code') # 取出原来的code cache_code=cache.get(settings.PHONE_CACHE_KEY%telephone) if code ==cache_code: # 验证码通过 if re.match('^1[3-9][0-9]{9}$', telephone): user = models.User.objects.filter(telephone=telephone).first() if user: # 把使用过的验证码删除 cache.set(settings.PHONE_CACHE_KEY % telephone,'') return user else: raise ValidationError('用户不存在') else: raise ValidationError('手机号不合法') else: raise ValidationError('验证码错误') def _get_token(self, user): from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler payload = jwt_payload_handler(user) # 通过user对象获得payload token = jwt_encode_handler(payload) # 通过payload获得token return token class UserRegisterSerilaizer(serializers.ModelSerializer): code=serializers.CharField(max_length=4,min_length=4,write_only=True) class Meta: model = models.User fields = ['telephone', 'code','password','username'] extra_kwargs = { 'password': {'max_length': 18,'min_length':8}, 'username': {'read_only':True} } # code单独校验的局部钩子(不好写,因为手机号不好取) def validate(self, attrs): telephone = attrs.get('telephone') code = attrs.get('code') # 取出原来的code cache_code = cache.get(settings.PHONE_CACHE_KEY % telephone) if code == cache_code or code=='1234': # 验证码通过 if re.match('^1[3-9][0-9]{9}$', telephone): attrs['username']=telephone # 把用户的名字设成手机号 attrs.pop('code') return attrs else: raise ValidationError('手机号不合法') else: raise ValidationError('验证码错误') # 重写create方法 def create(self, validated_data): user=models.User.objects.create_user(**validated_data) return user
from rest_framework.throttling import SimpleRateThrottle class SMSThrotting(SimpleRateThrottle): scope = 'sms' def get_cache_key(self, request, view): telephone = request.query_params.get('telephone') #'throttle_%(scope)s_%(ident)s'%{} return self.cache_format%{'scope':self.scope,'ident':telephone}
from django.urls import path, include from . import views from rest_framework.routers import SimpleRouter router = SimpleRouter() router.register('', views.LoginView, 'login') router.register('', views.SendSmSView, 'send') router.register('register', views.RegisterView, 'register') # /user/register post请求就是新增 urlpatterns = [ path('', include(router.urls)), ]
from django.shortcuts import render # Create your views here. from rest_framework.viewsets import ViewSet,GenericViewSet from rest_framework.mixins import CreateModelMixin from . import serializer from luffyapi.utils.response import APIResponse from rest_framework.decorators import action from . import models class LoginView(ViewSet): @action(methods=('post',), detail=False) def login(self, request, *args, **kwargs): ser = serializer.UserSerilaizer(data=request.data) if ser.is_valid(): token = ser.context['token'] # ser.context['user'] 是user对象 username = ser.context['user'].username # {'code':1 # msg:'chengg' # token:'sdfasdf' # username:'root' # } return APIResponse(token=token, username=username) else: return APIResponse(code=0, msg=ser.errors) @action(detail=False) def check_telephone(self, request, *args, **kwargs): import re telephone = request.query_params.get('telephone') if not re.match('^1[3-9][0-9]{9}', telephone): return APIResponse(code=0, msg='手机号不合法') try: models.User.objects.get(telephone=telephone) return APIResponse(code=1) except: return APIResponse(code=0,msg='手机号不存在') @action(methods=['POST'],detail=False) def code_login(self,request,*args,**kwargs): ser = serializer.CodeUserSerilaizer(data=request.data) if ser.is_valid(): token = ser.context['token'] username = ser.context['user'].username return APIResponse(token=token, username=username) else: return APIResponse(code=0,msg=ser.errors) from .throttlings import SMSThrotting class SendSmSView(ViewSet): throttle_classes = [SMSThrotting,] @action(methods=['GET'], detail=False) def send(self,request,*args,**kwargs): ''' 发送验证码接口 :return: ''' import re from luffyapi.libs.tx_sms import get_code,send_message from django.core.cache import cache from django.conf import settings telephone = request.query_params.get('telephone') if not re.match('^1[3-9][0-9]{9}', telephone): return APIResponse(code=0, msg='手机号不合法') code=get_code() result=send_message(telephone,code) # 验证码保存(保存到哪?) # sms_cache_%s cache.set(settings.PHONE_CACHE_KEY%telephone,code,180) if result: return APIResponse(code=1,msg='验证码发送成功') else: return APIResponse(code=0, msg='验证码发送失败') class RegisterView(GenericViewSet,CreateModelMixin): queryset = models.User.objects.all() serializer_class = serializer.UserRegisterSerilaizer # def create(self, request, *args, **kwargs): # ser=self.get_serializer(data=request.data) # if ser.is_valid(): # ser.save() # return APIResponse(code=1,msg='注册成功',username=ser.data.get('username')) # else: # return APIResponse(code=0, msg=ser.errors) # def create(self, request, *args, **kwargs): response=super().create(request, *args, **kwargs) username=response.data.get('username') return APIResponse(code=1,msg='注册成功',username=username)
.......
from .pay import alipay,gateway
from alipay import AliPay from . import setting alipay = AliPay( appid=setting.APPID, app_notify_url=None, # the default notify path app_private_key_string=setting.APP_PRIVATE_KEY_STRING, # alipay public key, do not use your own public key! alipay_public_key_string=setting.ALIPAY_PUBLIC_KEY_STRING, sign_type=setting.SIGN_TYPE, # RSA or RSA2 debug=setting.DEBUG # False by default ) gateway=setting.GATEWAY
import os APPID="2016092000554611" APP_PRIVATE_KEY_STRING = open(os.path.join(os.path.dirname(__file__),'pem','private_key.pem')).read() ALIPAY_PUBLIC_KEY_STRING = open(os.path.join(os.path.dirname(__file__),'pem','al_public_key.pem')).read() SIGN_TYPE='RSA2' DEBUG=True GATEWAY='https://openapi.alipaydev.com/gateway.do?' if DEBUG else 'https://openapi.alipay.com/gateway.do?'
from .send import get_code,send_message
from qcloudsms_py import SmsSingleSender from luffyapi.utils.logger import log from . import settings # 生成一个四位随机验证码 def get_code(): import random s_code='' for i in range(4): s_code+=str(random.randint(0,9)) return s_code def send_message(phone,code): ssender = SmsSingleSender(settings.appid, settings.appkey) params = [code, '3'] # 当模板没有参数时,`params = []` try: result = ssender.send_with_param(86, phone, settings.template_id, params, sign=settings.sms_sign, extend="", ext="") if result.get('result') == 0: return True else: return False except Exception as e: log.error('手机号:%s,短信发送失败,错误为:%s'%(phone,str(e)))
# 短信的配置 # 短信应用 SDK AppID appid = 1400397846 # SDK AppID 以1400开头 # 短信应用 SDK AppKey appkey = "fd972f6d5a15add46de47b50b8dbe930" # 短信模板ID,需要在短信控制台中申请 template_id = 661935 # NOTE: 这里的模板 ID`7839`只是示例,真实的模板 ID 需要在短信控制台中申请 # 签名 sms_sign = "小猿取经" # NOTE: 签名参数使用的是`签名内容`,而不是`签名ID`。这里的签名"腾讯云"只是示例,真实的签名需要在短信控制台中申请
.......
# 首页轮播图个数 BANNER_COUNTER=3 # 手机验证码缓存的key值 PHONE_CACHE_KEY='sms_cache_%s'
# 开发阶段配置文件 from .const import * import os #现在BASE_DIR内部的小的luffyapi BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 把这个路径加入到环境变量 import sys # print(sys.path) sys.path.insert(0,BASE_DIR) #把apps的路径加入到环境变量 sys.path.insert(1,os.path.join(BASE_DIR,'apps')) # print(sys.path) SECRET_KEY = 'm&7ghd_pbvje4^d7&fc+kigvlbg!*q*#&ubi^9yt$$m3^r3k)2' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'corsheaders', # xadmin主体模块 'xadmin', # 渲染表格模块 'crispy_forms', # 为模型通过版本控制,可以回滚数据 'reversion', 'django_filters', 'user',#因为apps目录已经被加到环境变量了,所以直接能找到 'home', 'course', 'order', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', # 处理跨域 'corsheaders.middleware.CorsMiddleware', # 'luffyapi.utils.middle.MyMiddle' ] from corsheaders.middleware import CorsMiddleware ROOT_URLCONF = 'luffyapi.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'luffyapi.wsgi.application' DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'luffyapi', 'USER':'luffyapi', 'PASSWORD':'Luffy123?', 'HOST':'127.0.0.1', 'PORT':3306 }, } import pymysql pymysql.install_as_MySQLdb() from django.contrib import auth AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai' USE_I18N = True USE_L10N = True USE_TZ = False # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.2/howto/static-files/ STATIC_URL = '/static/' MEDIA_URL='/media/' MEDIA_ROOT=os.path.join(BASE_DIR,'media') #现在的BASEDIR是luffyapi下的luffyapi AUTH_USER_MODEL='user.user' REST_FRAMEWORK={ 'EXCEPTION_HANDLER': 'luffyapi.utils.exceptions.common_exception_handler', } #日志的配置 LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'verbose': { 'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s' }, 'simple': { 'format': '%(levelname)s %(module)s %(lineno)d %(message)s' }, }, 'filters': { 'require_debug_true': { '()': 'django.utils.log.RequireDebugTrue', }, }, 'handlers': { 'console': { # 实际开发建议使用WARNING 'level': 'DEBUG', 'filters': ['require_debug_true'], 'class': 'logging.StreamHandler', 'formatter': 'simple' }, 'file': { # 实际开发建议使用ERROR 'level': 'INFO', 'class': 'logging.handlers.RotatingFileHandler', # 日志位置,日志文件名,日志保存目录必须手动创建,注:这里的文件路径要注意BASE_DIR代表的是小luffyapi 'filename': os.path.join(os.path.dirname(BASE_DIR), "logs", "luffy.log"), # 日志文件的最大值,这里我们设置300M 'maxBytes': 300 * 1024 * 1024, # 日志文件的数量,设置最大日志数量为10 'backupCount': 100, # 日志格式:详细格式 'formatter': 'verbose', # 文件内容编码 'encoding': 'utf-8' }, }, # 日志对象 'loggers': { 'django': { 'handlers': ['console', 'file'], 'propagate': True, # 是否让日志信息继续冒泡给其他的日志处理系统 }, } } CORS_ORIGIN_ALLOW_ALL = True CORS_ALLOW_METHODS = ( 'DELETE', 'GET', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'VIEW', ) CORS_ALLOW_HEADERS = ( 'authorization', 'content-type', ) import datetime JWT_AUTH = { # 过期时间7天 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), } REST_FRAMEWORK = { 'DEFAULT_THROTTLE_RATES':{ 'sms':'1/m' # key要跟类中的scop对应 } } # django默认不支持redis做缓存 # from django.core.cache.backends.filebased import FileBasedCache # from django_redis.cache import RedisCache # 缓存 CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "CONNECTION_POOL_KWARGS": {"max_connections": 100} # "PASSWORD": "123", } } } # 上线后必须换成公网地址 # 后台基URL BASE_URL = 'http://127.0.0.1:8000' # 前台基URL LUFFY_URL = 'http://127.0.0.1:8080' # 支付宝同步异步回调接口配置 # 后台异步回调接口 NOTIFY_URL = BASE_URL + "/order/success/" # 前台同步回调接口,没有 / 结尾 RETURN_URL = LUFFY_URL + "/pay/success"
#上线的配置文件 # 开发阶段配置文件 from .const import * import os #现在BASE_DIR内部的小的luffyapi BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 把这个路径加入到环境变量 import sys # print(sys.path) sys.path.insert(0,BASE_DIR) #把apps的路径加入到环境变量 sys.path.insert(1,os.path.join(BASE_DIR,'apps')) # print(sys.path) SECRET_KEY = 'm&7ghd_pbvje4^d7&fc+kigvlbg!*q*#&ubi^9yt$$m3^r3k)2' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = False # 运行的host,服务端的地址,买的服务器的公网ip ALLOWED_HOSTS = ['*'] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'corsheaders', # xadmin主体模块 'xadmin', # 渲染表格模块 'crispy_forms', # 为模型通过版本控制,可以回滚数据 'reversion', 'django_filters', 'user',#因为apps目录已经被加到环境变量了,所以直接能找到 'home', 'course', 'order', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', # 处理跨域 'corsheaders.middleware.CorsMiddleware', # 'luffyapi.utils.middle.MyMiddle' ] from corsheaders.middleware import CorsMiddleware ROOT_URLCONF = 'luffyapi.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'luffyapi.wsgi.application' DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'luffyapi', 'USER':'luffyapi', 'PASSWORD':'Luffy123?', 'HOST':'127.0.0.1', 'PORT':3306 }, } import pymysql pymysql.install_as_MySQLdb() from django.contrib import auth AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai' USE_I18N = True USE_L10N = True USE_TZ = False # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.2/howto/static-files/ STATIC_URL = '/static/' MEDIA_URL='/media/' MEDIA_ROOT=os.path.join(BASE_DIR,'media') #现在的BASEDIR是luffyapi下的luffyapi AUTH_USER_MODEL='user.user' REST_FRAMEWORK={ 'EXCEPTION_HANDLER': 'luffyapi.utils.exceptions.common_exception_handler', } #日志的配置 LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'verbose': { 'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s' }, 'simple': { 'format': '%(levelname)s %(module)s %(lineno)d %(message)s' }, }, 'filters': { 'require_debug_true': { '()': 'django.utils.log.RequireDebugTrue', }, }, 'handlers': { 'console': { # 实际开发建议使用WARNING 'level': 'DEBUG', 'filters': ['require_debug_true'], 'class': 'logging.StreamHandler', 'formatter': 'simple' }, 'file': { # 实际开发建议使用ERROR 'level': 'INFO', 'class': 'logging.handlers.RotatingFileHandler', # 日志位置,日志文件名,日志保存目录必须手动创建,注:这里的文件路径要注意BASE_DIR代表的是小luffyapi 'filename': os.path.join(os.path.dirname(BASE_DIR), "logs", "luffy.log"), # 日志文件的最大值,这里我们设置300M 'maxBytes': 300 * 1024 * 1024, # 日志文件的数量,设置最大日志数量为10 'backupCount': 100, # 日志格式:详细格式 'formatter': 'verbose', # 文件内容编码 'encoding': 'utf-8' }, }, # 日志对象 'loggers': { 'django': { 'handlers': ['console', 'file'], 'propagate': True, # 是否让日志信息继续冒泡给其他的日志处理系统 }, } } CORS_ORIGIN_ALLOW_ALL = True CORS_ALLOW_METHODS = ( 'DELETE', 'GET', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'VIEW', ) CORS_ALLOW_HEADERS = ( 'authorization', 'content-type', ) import datetime JWT_AUTH = { # 过期时间7天 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), } REST_FRAMEWORK = { 'DEFAULT_THROTTLE_RATES':{ 'sms':'1/m' # key要跟类中的scop对应 } } # django默认不支持redis做缓存 # from django.core.cache.backends.filebased import FileBasedCache # from django_redis.cache import RedisCache # 缓存 CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "CONNECTION_POOL_KWARGS": {"max_connections": 100} # "PASSWORD": "123", } } } # 上线后必须换成公网地址 # 后台基URL BASE_URL = 'http://47.103.156.13:8000' # 前台基URL LUFFY_URL = 'http://47.103.156.13' # 支付宝同步异步回调接口配置 # 后台异步回调接口 NOTIFY_URL = BASE_URL + "/order/success/" # 前台同步回调接口,没有 / 结尾 RETURN_URL = LUFFY_URL + "/pay/success"
.......
#方法,加日志 from rest_framework.views import exception_handler # from luffyapi.utils import response from .response import APIResponse from .logger import log def common_exception_handler(exc, context): log.error('view是:%s ,错误是%s'%(context['view'].__class__.__name__,str(exc))) #context['view'] 是TextView的对象,想拿出这个对象对应的类名 print(context['view'].__class__.__name__) ret=exception_handler(exc, context) # 是Response对象,它内部有个data if not ret: #drf内置处理不了,丢给django 的,我们自己来处理 # 好多逻辑,更具体的捕获异常 if isinstance(exc,KeyError): return APIResponse(code=0, msg='key error') return APIResponse(code=0,msg='error',result=str(exc)) else: return APIResponse(code=0,msg='error',result=ret.data)
import logging # log=logging.getLogger('名字') # 跟配置文件中loggers日志对象下的名字对应 log=logging.getLogger('django')
from django.middleware.csrf import CsrfViewMiddleware from django.utils.deprecation import MiddlewareMixin class MyMiddle(MiddlewareMixin): def process_response(self, request, response): response['Access-Control-Allow-Origin'] = '*' if request.method == "OPTIONS": # 可以加* response["Access-Control-Allow-Headers"] = "Content-Type" response["Access-Control-Allow-Headers"] = "authorization" return response
from django.db import models # 后期课程表,轮播图表,都会用到这些字段 class BaseModel(models.Model): is_delete = models.BooleanField(default=False, verbose_name='是否删除') is_show = models.BooleanField(default=True, verbose_name='是否展示') # 修改成这样 created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') updated_time = models.DateTimeField(auto_now=True, verbose_name='最后更新时间') orders = models.IntegerField() class Meta: abstract=True # 一定不要忘了
from rest_framework.response import Response class APIResponse(Response): def __init__(self,code=1,msg='成功',result=None,status=None,headers=None,content_type=None,**kwargs): dic={ 'code':code, 'msg':msg } if result: dic['result']=result dic.update(kwargs) #对象来调用对象的绑定方法,会自动传值 super().__init__(data=dic,status=status,headers=headers,content_type=content_type) # 类来调用对象的绑定方法,这个方法就是一个普通函数,有几个参数就要传几个参数 # Response.__init__(data=dic,status=status,headers=headers,content_type=content_type)
.......
from django.contrib import admin from django.urls import path, re_path, include from django.views.static import serve from django.conf import settings # xadmin的依赖 import xadmin xadmin.autodiscover() # xversion模块自动注册需要版本控制的 Model from xadmin.plugins import xversion xversion.register_models() urlpatterns = [ path('xadmin/', xadmin.site.urls), path('home/', include('home.urls')), path('user/', include('user.urls')), path('course/', include('course.urls')), path('order/', include('order.urls')), # media文件夹路径打开了 re_path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}), ]
""" WSGI config for luffyapi project. It exposes the WSGI callable as a module-level variable named ``application``. For more information on this file, see https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/ """ import os from django.core.wsgi import get_wsgi_application os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffyapi.settings.pro') application = get_wsgi_application()
.......
.idea
logs
scripts
*.pyc
#!/usr/bin/env python """Django's command-line utility for administrative tasks.""" import os import sys def main(): os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffyapi.settings.dev') try: from django.core.management import execute_from_command_line except ImportError as exc: raise ImportError( "Couldn't import Django. Are you sure it's installed and " "available on your PYTHONPATH environment variable? Did you " "forget to activate a virtual environment?" ) from exc execute_from_command_line(sys.argv) if __name__ == '__main__': main()
#!/usr/bin/env python """Django's command-line utility for administrative tasks.""" import os import sys def main(): os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffyapi.settings.pro') try: from django.core.management import execute_from_command_line except ImportError as exc: raise ImportError( "Couldn't import Django. Are you sure it's installed and " "available on your PYTHONPATH environment variable? Did you " "forget to activate a virtual environment?" ) from exc execute_from_command_line(sys.argv) if __name__ == '__main__': main()
celery==4.4.6 Django==2.0.7 django-cors-headers==3.4.0 django-filter django-redis qcloudsms_py djangorestframework==3.11.0 djangorestframework-jwt==1.11.0 Pillow==7.2.0 PyMySQL==0.9.3 python-alipay-sdk==2.0.1 redis==3.5.3 xadmin @ https://codeload.github.com/sshwsfc/xadmin/zip/django2
.......
project ├── celery_task # celery包 │ ├── __init__.py # 包文件 │ ├── celery.py # celery连接和配置相关文件,且名字必须叫celery.py │ ├── task1.py # 任务函数 │ └── task2.py # 任务函数 ├── t_celery_add_task.py # 添加任务 └── t_get_result.py # 获取结果
from celery import Celery broker='redis://127.0.0.1:6379/1' #broker任务队列 backend='redis://127.0.0.1:6379/2' # 结构存储,执行完的结果存在这 app=Celery(__name__,broker=broker,backend=backend,include=['celery_task.task1','celery_task.task2']) # 执行定时任务 # 时区 app.conf.timezone = 'Asia/Shanghai' # 是否使用UTC app.conf.enable_utc = False # 任务的定时配置 from datetime import timedelta from celery.schedules import crontab app.conf.beat_schedule = { 'add-task': { 'task': 'celery_task.task1.add', # 'schedule': timedelta(seconds=3), 'schedule': crontab(hour=8, day_of_week=1), # 每周一早八点 'args': (300, 150), } } # 一定要启动beat # celery beat -A celery_task -l info
from .celery import app @app.task def add(x,y): print(x,y) return x+y # @app.task # def updata_banner(): # # # 在脚本中调用django项目 # os.environ.setdefault("DJANGO_SETTINGS_MODULE", "untitled15.settings") # import django # django.setup() # # from app01 import models # from django.core.cache import cache # books = models.Book.objects.all() # print(books) # 用命令来执行 # 非windows # 命令:celery worker -A celery_task -l info # windows: # pip3 install eventlet # celery worker -A celery_task -l info -P eventlet
from .celery import app @app.task def mutile(x,y): print(x,y) return x*y
from celery_task.task1 import add from celery_task.task2 import mutile # ret=add(6,7) # print(ret) # # 提交任务 # ret=add.delay(6,7) # print(ret) # 2d4ad592-9548-4c7c-8df4-7f8583e8a1b1 # # ret=mutile.delay(9,9) # print(ret) # 执行延迟任务 # 添加延迟任务 from datetime import datetime, timedelta # print(datetime.utcnow()) # print(type(timedelta(seconds=10))) # eta=datetime.utcnow() + timedelta(days=10) # print(eta) # print(type(eta)) # # # # 需要utc时间 # eta=datetime.utcnow() + timedelta(seconds=10) # ret=add.apply_async(args=(240, 50), eta=eta) # print(ret)
from celery_task.celery import app from celery.result import AsyncResult id = '01e57ace-03b2-483b-9056-8e7289537e07' if __name__ == '__main__': async_result=AsyncResult(id=id, app=app) if async_result.successful(): result = async_result.get() print(result) elif async_result.failed(): print('任务失败') elif async_result.status == 'PENDING': print('任务等待中被执行') elif async_result.status == 'RETRY': print('任务异常后正在重试') elif async_result.status == 'STARTED': print('任务已经开始被执行')
.......
# # 简单使用 # from redis import Redis # conn=Redis(host='127.0.0.1', port=6379,decode_responses=True) # conn.lpush('list1',999) #redis连接池 import redis #pool必须是单例的 POOL = redis.ConnectionPool(host='127.0.0.1', port=6379,max_connections=100) # 造一个池子,最多能放100个连接
# 简单使用 from redis import Redis # conn=Redis() # # conn=Redis.from_url('redis://127.0.0.1:6379/2') # 为什么要写成类的绑定方法 #连接对象 # conn=Redis(host='127.0.0.1', port=6379,decode_responses=True) # ret=conn.get('name') # print(ret) # #redis连接池 import redis # #pool必须是单例的 # pool = redis.ConnectionPool(host='127.0.0.1', port=6379,max_connections=100) # 造一个池子,最多能放100个连接 # import redis # # #包内的py文件,如果想右键运行,导包的时候不能带点 from t_redis_pool import POOL # pycharm提示的错 conn = redis.Redis(connection_pool=POOL) # 只要执行这一句话,就是从池中拿出一个连接 # r = redis.Redis(connection_pool=POOL) # 只要执行这一句话,就是从池中拿出一个连接 # r = redis.Redis(connection_pool=POOL) # 只要执行这一句话,就是从池中拿出一个连接 # r = redis.Redis(connection_pool=POOL) # 只要执行这一句话,就是从池中拿出一个连接 # ret=r.get('name') # print(ret) #####字符串操作 ####1 set的用法 # conn.set('height',180) #基本使用 # conn.set('height','190',nx=True) # conn.set('height','190',xx=True) # conn.set('height1','190',xx=True) ''' ex,过期时间(秒) px,过期时间(毫秒) nx,如果设置为True,则只有name不存在时,当前set操作才执行,值存在,就修改不了,执行没效果 xx,如果设置为True,则只有name存在时,当前set操作才执行,值存在才能修改,值不存在,不会设置新值 ''' ### 2 # setnx(name, value) # # 设置值,只有name不存在时,执行设置操作(添加),如果存在,不会修改 # setex(name, value, time) # # 设置值 # # 参数: # # time,过期时间(数字秒 或 timedelta对象) # psetex(name, time_ms, value) # # 设置值 # # 参数: # # time_ms,过期时间(数字毫秒 或 timedelta对象 # mset # conn.mset({'name1':'11','name3':'dasfd'}) # ret=conn.mget(['name1','name','name3']) # print(ret) # ret=conn.getset('name1', '999') # print(ret) # ret=conn.getrange('name1',0,0) # 前闭后闭区间 # print(ret) # conn.setrange('name1',1,88888) # ret=conn.getbit('name1',9) # print(ret) #incr :统计网站访问量,页面访问量,接口访问量 # conn.incr('name1') # 只要一执行,数字加1 # conn.incr('name1') # 只要一执行,数字加1 # conn.incr('name1') # 只要一执行,数字加1 # conn.incr('name1') # 只要一执行,数字加1 #decr # conn.incr('name1',-2) # conn.decr('name1',3) # conn.append('name1','oo') # conn.incr('name1') #set :很多参数 #get #mset #mget #incr #decr #append # hash操作 # conn.hset('hash1','name','lqz') # conn.hset('hash1','name2','lqz') # conn.hset('hash1','name','lqz444') # key不可以重复, # ret=conn.hget('hash1','name') #只能取一个 # print(ret) # conn.hmset('hash2',{'key1':'value1','key2':'value2'}) # ret=conn.hmget('hash1','name','name2') # ret=conn.hmget('hash1',['name','name2']) # print(ret) # ret=conn.hgetall('hash1') # 尽量少用 # print(ret) # ret=conn.hlen('hash1') # ret=conn.hkeys('hash1') # ret=conn.hexists('hash1','name1') # ret=conn.hdel('hash1','name') # conn.hset('hash1','name',12) # ret=conn.hincrby('hash1','name') # # print(ret) # 以后想取出hash类型内所有的数据,不建议用hgetall,建议用hscan_iter # 一次性先取一部分回来(假设有1w条,先取回100条,把这100条做成了生成器) # ret=conn.hscan_iter('hash1') # print(ret) # for i in ret: # print(i) # hset # hget #hmset #hmget # hincrby # 区分hgetall和hscan_iter ### 列表操作 # ret=conn.lpush('list1',1,2,3,4,5) # ret=conn.rpush('list1',999) # ret=conn.lpushx('list2',1) # ret=conn.lpushx('list1',888) # 必须有这个key才能放 # ret=conn.rpushx('list1',666) # 我们猜,返回总长度 # ret=conn.llen('list1') # ret=conn.linsert('list1','before','3','77777777') # ret=conn.linsert('list1','after','3','66666666') # ret=conn.lset('list1',3,'22222') #从0开始计数 # ret=conn.lset('list1',0,'11111') # ret=conn.lrem('list1',2,'5') # 从前往后删除两个5 # ret=conn.lrem('list1',-1,'5') # 从后往前删除1个5 # ret=conn.lrem('list1',0,'5') # 删除所有5 # ret=conn.lpop('list1') # ret=conn.rpop('list1') # ret=conn.lindex('list1',0) # ret=conn.lrange('list1',0,2) # 前闭后闭 # ret=conn.ltrim('list1',1,2) # 重点block,阻塞,可以写一个超时时间 # ret=conn.blpop('list1',timeout=10) # print(ret) # 自定制分批取列表的数据 # conn.lpush('test',*[1,2,3,4,45,5,6,7,7,8,43,5,6,768,89,9,65,4,23,54,6757,8,68]) # conn.flushall() # def scan_list(name,count=2): # index=0 # while True: # data_list=conn.lrange(name,index,count+index-1) # if not data_list: # return # index+=count # for item in data_list: # yield item # # print(conn.lrange('test',0,100)) # for item in scan_list('test',5): # print('---') # print(item) # 管道。实现事务 # import redis # pool = redis.ConnectionPool(host='127.0.0.1', port=6379) # # conn = redis.Redis(connection_pool=pool) # # # pipe = r.pipeline(transaction=False) # pipe = conn.pipeline(transaction=True) # pipe.multi() # pipe.set('name', 'alex') # # pipe.set('role', 'sb') # # pipe.execute() # 这句话,才真正的去执行 # 其他操作 # conn.delete('list1') # ret=conn.delete('hash1') # ret=conn.exists('hash2') # ret=conn.keys('cache*') #查询以cache开头的所有key # ret=conn.expire('hash2',2) # ret=conn.type('name3') # ret=conn.type('test') # ret=conn.type('test') # print(ret)
############################################################################ # ValueError: not enough values to unpack (expected 3, got 0) # # 可使用celery -A celery_task worker --pool=solo -l info 解决 # 从4.3.0到4.4.0的Kombu更新停止了对redis-py v2.10.6的支持,因此迫使我们升级redis-py版本。 # # 所以我们要修改自己的requeirement.txt文件中的kombu的版本了 # # # kombu版本 # pip install kombu==4.2.1 # # 如果你也使用了celery # pip install celery==4.1.1 # pip install redis ==2.10.5 # python3.5 ############################################################################
from alipay import AliPay # app_private_key_string = open("/path/to/your/private/key.pem").read() # alipay_public_key_string = open("/path/to/alipay/public/key.pem").read() app_private_key_string = """-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEApkFnp38PSslhn4wvRarL40+WtVpan6tVT55FWzAzLfQ8PfqZE2qW7mCJy1/zM07+S5L+X0pC+F7BlT+0z4rlxraG5D23khWfHb4x+uDi0Wd94eaVB0PzQXCYYBChuORvNN5YLTspeP7rNQ2OcdpGx2KUzo9w59dVW+AweblFl7GqPkRSC0P5XbkJtwnEAd0Nc08HzJR0OWZT9HRFOA7coaude9uSwDsXmsdkSNGsGDcaGEY6aGk2Tj9Fyni9LJJcOXwGrUuGDpdcP2tYePrFUGMDml/feFR9QGUbExWh/ZNpb9lKLwOtXOhQj9slilp+YJek/rLlSgW9K1WrYpuuBwIDAQABAoIBADN1mRzKAjS2wmW84UDiDbus/cviTJyRTpWXOoZwE9dMen0AnPLakh70eJIff8pI0AMaW2upM7NmuOp2ToPSzS5FftkUlUY9NQPiw9uQUgRY0Sjj0wrtqFSAAlnxq+zrn9QwYgCWCE8wMCM6r/Vjh3bdd4u78EmCaCRI7xguFXFPB9NY9oCFiwsTFJtXPZo+DSIQjqIDnh8YtV3NzrA3Ln3DsKMAr+vnPMeljAL5US6Rt5tOQY0qa9bUi6RqQwTLADcANd49YOvlr7OLjwi5jkeJ16j1nKgpasAIg9V+rF085u+PEKkLL3WJHwmhiIxrA11bxCnm1fRdfHbyWZKY9yECgYEA0k3wS7A7rzhtpznc5ZfVGWIEQ5IahRkqVGbc6wFIXKXnypOtGKAFiRmEPwm9xRurlo8TJ1bjK4D4ljEANJhX2NnIZfK3YDdNEN6D4GnEvPFR641M8M47Z7ItvdRKre8P2/ThXhkMnq5/1y7e87xvE6/a1Tr44+hclHvQWPRrsPcCgYEAymFEy0/2xAX+si4S8pzrgLfBg3Ehjan8T6JZZNCKhR7b/IT+3/AWJimzEL6jYQMlhy7QzZbRb1ssIIzqPFcFQYjQE0n6fswJuN4+jayW9Jtjvar/zhHFjW3EODR5yEDTvS3CHqQLeG3ce+srMJSxVDJR2V9ortFeb+VPbWZ+t3ECgYEAxPYvvoNwcpuzvvGnW/RGpb4x5iL46Xz3MxMfho2t+u961jRW4oBEjvGx9OQnsmpG2vxm4Oo0WnMw3mFIIvonFDZrxGd8rQU+DTWJZ21Hz/lnUugEjmdoJacvxeEEjEAgp02CoQFu21Ls8li4gKgTk+mYVyojHjhqNLp9GELadWMCgYEAi0RSXgLCEnT5p03zdgcsPOC3ByfT6jO+0GItWCX2HNN2mRhAeIQ0CcEKW4yEy56ptZQu1jtiFlpMTH4MNse/czCd15hCC/2G9zPhIgdRvjQsd/nznLA4HTIbJH5gC8EotHeHrSRATHh1kMTtbLn2KbWTA54XYK3tad0IQoWUz9ECgYBBc+kCJsb0shaRIRP5nmeb5tZCH7Yq9DgriwcIlK74cxW9fvL94n91Y6M8vSZ6MC7vF5wmcXMj3K1of04qizKbbKxCCYeOIHhTh0dB+ualDxu5WBG/6mLWR3HVHazqRVri9qoZ/3zNDx5CRNi81VpzOPmVtuos5d9sZZaNR3lkRQ== -----END RSA PRIVATE KEY----- """ alipay_public_key_string = """-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwU9mMZHHlQPE9FcxVtXOXhbWCtuDZLJRVCiofdbTVmRXrx47yGniPehwcKIsqhzhEaXBG2QhpIZUL8YsCav0mkrppoRvWOytuGyxNRESo8I6DWRs0aCq6P3AuiD9kSXET4dpAuRYT/+JrMXIZTycEts6vYYNAT9QivXJoa2FmiCQBAL3HP7F36pby9VstObilxXQcoBBJwEYGf2TK6moFFZ1dkloRr5Cfk/G82DpuVfrt1gr4OuIDWtcE3MZTrvDgTqtkRuwGF76FY3+8xUCUbJs1dL5cXYN7/b3jPcXVcdKXFj4WrOQd42ofE1BJWMxBW7L3Qlxue1vy+NGx/CuKwIDAQAB -----END PUBLIC KEY----- """ alipay = AliPay( appid="2016092000554611", app_notify_url='http://127.0.0.1:8000/home/', # the default notify path app_private_key_string=app_private_key_string, # alipay public key, do not use your own public key! alipay_public_key_string=alipay_public_key_string, sign_type="RSA2", # RSA or RSA2 debug=True # False by default ) alipay_url='https://openapi.alipaydev.com/gateway.do?' order_string = alipay.api_alipay_trade_page_pay ( out_trade_no="20161s112www4334", total_amount=9999, subject='笔记本电脑', return_url="https://www.luffycity.com/free-course", notify_url="https://www.luffycity.com/free-course" ) print(alipay_url+order_string)
# 短信应用 SDK AppID appid = 1400397846 # SDK AppID 以1400开头 # 短信应用 SDK AppKey appkey = "fd972f6d5a15add46de47b50b8dbe930" # 需要发送短信的手机号码 ll=['17681790058','18701244561'] # 短信模板ID,需要在短信控制台中申请 template_id = 661935 # NOTE: 这里的模板 ID`7839`只是示例,真实的模板 ID 需要在短信控制台中申请 # 签名 sms_sign = "小猿取经" # NOTE: 签名参数使用的是`签名内容`,而不是`签名ID`。这里的签名"腾讯云"只是示例,真实的签名需要在短信控制台中申请 from qcloudsms_py import SmsSingleSender from qcloudsms_py.httpclient import HTTPError ssender = SmsSingleSender(appid, appkey) params = ["1234",'3'] # 当模板没有参数时,`params = []` try: result = ssender.send_with_param(86, ll[0],template_id, params, sign=sms_sign, extend="", ext="") result = ssender.send_with_param(86, ll[1],template_id, params, sign=sms_sign, extend="", ext="") if result.get('result')==0: pass except HTTPError as e: print(e) except Exception as e: print(e)