• 基于rest_framework和redis实现购物车的操作,结算,支付


    前奏:

      首先,要在主机中安装redis,windows中安装,下载一个镜像,直接进行下一步的安装,安装成功后,在cmd中输入redis-cli

      安装python的依赖库: redis     和   django-redis

      redis是一个python的库,用来操作redis。

      django默认支持的缓存是memcache,使用redis作为django的缓存,就需要django-redis了,django-redis是一个开源的库。

    只需要在全局settings中配置即可。

    django-redis的配置使用

    在settings.py中的配置:

    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": "密码",
            }
        }
      "可自定义配置":{...} }
    在视图中的使用:
    from django_redis import get_redis_connection
    conn = get_redis_connection("default")

      安装配置后就可以使用了,但是据说性能不够高,官方文档见https://niwinz.github.io/django-redis/latest/

     异常错误信息的自定义

    # 自定义一个类名,这个类继承Exception即可
    class PriceNoExistsError(Exception):
    
        def __init__(self,msg):
            self.msg = msg

    模型表数据存储的校验

    models.py中模型表可以在数据保存前做数据校验,重写save方法即可

    比如:校验优惠券存储的开始和结束时间

    class Coupon(models.Model):
        """优惠券生成规则"""
        name = models.CharField(max_length=64, verbose_name="活动名称")
        brief = models.TextField(blank=True, null=True, verbose_name="优惠券介绍")
        coupon_type_choices = ((0, '通用券'), (1, '满减券'), (2, '折扣券'))
        coupon_type = models.SmallIntegerField(choices=coupon_type_choices, default=0, verbose_name="券类型")
    
        money_equivalent_value = models.IntegerField(verbose_name="等值货币",blank=True,null=True)
        off_percent = models.PositiveSmallIntegerField("折扣百分比", help_text="只针对折扣券,例7.9折,写79", blank=True, null=True)
        minimum_consume = models.PositiveIntegerField("最低消费", default=0, help_text="仅在满减券时填写此字段",blank=True,null=True)
    
        content_type = models.ForeignKey(ContentType, blank=True, null=True)
        object_id = models.PositiveIntegerField("绑定课程", blank=True, null=True, help_text="可以把优惠券跟课程绑定")
        content_object = GenericForeignKey('content_type', 'object_id')
    
        quantity = models.PositiveIntegerField("数量(张)", default=1)
        open_date = models.DateField("优惠券领取开始时间")
        close_date = models.DateField("优惠券领取结束时间")
        valid_begin_date = models.DateField(verbose_name="有效期开始时间", blank=True, null=True)
        valid_end_date = models.DateField(verbose_name="有效结束时间", blank=True, null=True)
        coupon_valid_days = models.PositiveIntegerField(verbose_name="优惠券有效期(天)", blank=True, null=True,
                                                        help_text="自券被领时开始算起")
        date = models.DateTimeField(auto_now_add=True)
    
        class Meta:
            verbose_name_plural = "31. 优惠券生成记录"
    
        def __str__(self):
            return "%s(%s)" % (self.get_coupon_type_display(), self.name)
    
        def save(self, *args, **kwargs):
            if not self.coupon_valid_days or (self.valid_begin_date and self.valid_end_date):
                if self.valid_begin_date and self.valid_end_date:
                    if self.valid_end_date <= self.valid_begin_date:
                        raise ValueError("valid_end_date 有效期结束日期必须晚于 valid_begin_date ")
                if self.coupon_valid_days == 0:
                    raise ValueError("coupon_valid_days 有效期不能为0")
            if self.close_date < self.open_date:
                raise ValueError("close_date 优惠券领取结束时间必须晚于 open_date优惠券领取开始时间 ")
    
            super(Coupon, self).save(*args, **kwargs)

    对于购物车的操作,结算,支付等部分视图函数做登录认证的处理

    借助rest_framework的局部认证功能

    from api import models
    from rest_framework.authentication import BaseAuthentication
    from rest_framework.exceptions import AuthenticationFailed
    
    
    class LoginAuth(BaseAuthentication):
    
        def authenticate(self, request):
            token = request.query_params.get('token')
            token_obj = models.Token.objects.filter(name=token).first()
            if token_obj:
                return token_obj.user,token_obj
            else:
                raise AuthenticationFailed('认证失败')

    封装响应数据的结构

    可以将所有视图函数返回给前端的响应信息封装到一个类中,什么时候需要直接实例化对象使用。

    class StatusRet(object):
        '''构建返回响应的结构''
    
        def __init__(self):
            self.code = 1000
            self.msg = ''
            self.data = None
    
        @property
        def dict(self):
            return self.__dict__

    借助admin快速添加数据

    from django.contrib import admin
    
    # Register your models here.
    
    from django.apps import apps
    
    all_model_gene =apps.get_app_config('api').get_models()   # 将所有的模型类放在生成器中
    for model_cls in all_model_gene:
        admin.site.register(model_cls)
    from django.contrib import admin

    from
    django.apps import apps app_models =apps.get_app_config("api").get_models() from django.contrib.admin.sites import AlreadyRegistered for i in app_models: try: admin.site.register(i) except AlreadyRegistered: pass

    购物车的增删改查:

    1.url的设计

      url(r'^shopping_car/',views.ShoppingCar.as_view()),2.视图类的处理

    redis中数据的存储格式:  通过这种name的形式来存储 哪个用户的哪门课程信息

    SHOP_CAR_COURSE_KEY = 'shop_car_%s_%s'     # 第一个%s 代表当前的用户的主键id   第二个%s 代表某个课程的主键id

    具体的视图函数逻辑:

    class ShoppingCar(APIView):
        '''购物车的增删改查'''
    
        #  购物车的登录认证
        authentication_classes = [LoginAuth]
       conn
    = get_redis_connection('default') def get(self,request): '''获取当前用户在购物车中的所有记录''' # 获取返回值对象 ret = StatusRet() # 从redis中获取当前用户所有的key shop_car_key = settings.SHOP_CAR_COURSE_KEY % (request.user.pk, '*') # 利用scan_iter 将当前用户所有的keys生成一个生成器 price_policy_gene = self.conn.scan_iter(shop_car_key) # 构建接口数据 ''' [ { key1:{ title:...,price:... }}, { key2:{... }},... ] ''' price_policy_list = [] for keys in price_policy_gene: data_dict = { 'title': self.conn.hget(keys,'title').decode(), 'price': self.conn.hget(keys,'price').decode(), 'course_img': self.conn.hget(keys,'course_img').decode(), 'price_policy': json.loads(self.conn.hget(keys,'price_policy').decode()), # 直接json反序列化 'checked_price_policy_id': self.conn.hget(keys,'checked_price_policy_id').decode() } price_policy_list.append(data_dict) # 将查询的所有数据返回 ret.data = price_policy_list return Response(ret.dict) def post(self,request): '''添加购物车记录''' ret = StatusRet() # 获取从前端发来的数据 course_id : 课程的pk值 price_policy_id : 价格策略的pk值 course_id = request.data.get('course_id') price_policy_id = request.data.get('price_policy_id') # 校验前端传来数据的合法性 try: course_obj = models.Courses.objects.get(pk=course_id) price_policy_list = course_obj.price_policy.all() price_policy_dict = {} for item in price_policy_list: price_policy_dict[item.pk] = {"name":item.name,"price":float(item.price)} if int(price_policy_id) not in price_policy_dict: raise PriceNoExistsError('价格策略不存在') shop_car_key = settings.SHOP_CAR_COURSE_KEY%(request.user.pk,course_id) # 构建数据 val_dict = { "title":course_obj.title, "price":course_obj.price, "course_img":course_obj.course_img, "price_policy":json.dumps(price_policy_dict,ensure_ascii=False), #提前将数据json序列化,便于后期的取值 "checked_price_policy_id":price_policy_id } # 将数据添加到redis中 self.conn.hmset(shop_car_key,val_dict) ret.data = val_dict except PriceNoExistsError as e: ret.code = 2000 ret.msg = str(e) except ObjectDoesNotExist as e: ret.code = 2000 ret.msg = '课程不存在' except Exception as e: ret.code = 2000 ret.msg = '出错了' return Response(ret.dict) def put(self,request): '''修改购物车记录''' ret = StatusRet() # 获取前端传来的数据 course_id = request.data.get('course_id') price_policy_id = request.data.get('price_policy_id') # 校验数据的合法性 try: shop_car_key = settings.SHOP_CAR_COURSE_KEY % (request.user.pk, course_id) if not self.conn.exists(shop_car_key): raise CourseNoExistsErrot('修改课程不存在') course_data = self.conn.hgetall(shop_car_key) course_policy = json.loads(self.conn.hget(shop_car_key,"price_policy").decode()) if str(price_policy_id) not in course_policy: raise PriceNoExistsError('价格策略不存在') # 将redis中的值进行修改 self.conn.hset(shop_car_key,'checked_price_policy_id',price_policy_id) # 构建返回给前端的数据 return_data = {} for k,v in course_data.items(): if k == b"price_policy": val = json.loads(v.decode()) else: val = v.decode() return_data[k.decode()] = val ret.data = return_data except CourseNoExistsErrot as e: ret.code = 2000 ret.msg = str(e) except PriceNoExistsError as e: ret.code = 2000 ret.msg = str(e) except Exception as e: ret.code = 2000 ret.msg = '更新失败' return Response(ret.dict) def delete(self,request): '''删除购物车记录''' ret = StatusRet() # 校验前端传来数据的合法性 try: course_id_list = request.data.get('course_id') shop_key_list =[] for course_id in course_id_list: shop_car_key = settings.SHOP_CAR_COURSE_KEY % (request.user.pk, course_id) if not self.conn.exists(shop_car_key): raise CourseNoExistsErrot('删除课程不存在') else: # 将所有校验合法的key存在列表中 shop_key_list.append(shop_car_key) # 从redis中删除记录 self.conn.delete(*shop_key_list) ret.data = '' except CourseNoExistsErrot as e: ret.code = 2000 ret.msg = str(e) return Response(ret.dict)

     备注:使用json进行序列化时,如果字段类型为decimal时,要先将数据转为float类型,否则json无法序列化,json不支持decimal的数据类型。

    结算页面

    购物车中增删改查实现后,就是结算页面了,结算包含两种情况,一种是购物车中选中商品后的直接结算,是post请求,将该商品的所有信息和所有可用的优惠券(包含专用的和通用的),以及其他,比如散币(腾讯的Q币,淘宝的积分,等等)查询出来后存入redis中,并将数据构建后返回。另一种是用户选择结算后的延迟,在此使用时,可以通过未支付页面的get请求,直接从redis中取数据,并返回给前端的展示。

    1.url的构建

        url(r'^settle_center/',views.SettlementView.as_view()),

    2.redis中的存储方式:

    PAYMENT_COURSE_KEY = 'payment_%s_%s'  #课程专用优惠券
    PAYMENT_COMMON_KEY = 'pay_common_%s'    #通用优惠券
    USER_BALANCE_COUNT = 'balance_count_%s'   #用户贝里数

    3.视图逻辑

    class SettlementView(APIView):
        '''结算的逻辑操作'''
        authentication_classes = [LoginAuth]  #登录认证
        conn = get_redis_connection('default')   #redis的连接
    
        def get_return_data(self,request):
            '''构建返回给前端的数据'''
    
            #获取用户pk值
            user_id = request.user.pk
            #获取redis中该用户的贝里数
            user_balance_key = settings.USER_BALANCE_COUNT
            user_balance_count = user_balance_key % user_id
            balance_data = self.conn.hgetall(user_balance_count)
            # 获取redis中该用户的所有课程专用优惠券
            payment_key = settings.PAYMENT_COURSE_KEY % (user_id, '*')
            keys = self.conn.scan_iter(payment_key)
            #获取redis中该用户的通用优惠券
            common_key = settings.PAYMENT_COMMON_KEY % user_id
            common_data = self.conn.hgetall(common_key)
    
            return_data = []
            # 构建贝里数数据
            balance_dict = {}
            for k,v in balance_data.items():
                balance_dict[k.decode()] = v.decode()
            # 构建通用优惠券的信息
            common_show_dict = {}
            for com_k, com_v in common_data.items():
                common_show_dict[com_k.decode()] = json.loads(com_v.decode())
    
            # 处理普通优惠券的数据
            coupon_dict = {}
            for key in keys:
                course_id = key.decode().split('_')[-1]
                coupon_dict[course_id] = {}
                course_coupon_data = self.conn.hgetall(key)
                for cou_k, cou_v in course_coupon_data.items():
                    if cou_k == b'coupons':
                        coupons_data = {}
                        for coup_k, coup_v in json.loads(cou_v.decode()).items():
                            coupons_data[coup_k] = json.loads(coup_v)
                        coupon_dict[course_id][cou_k.decode()] = coupons_data
                    else:
                        coupon_dict[course_id][cou_k.decode()] = json.loads(cou_v.decode())
    
            return_data.append(balance_dict)
            return_data.append(common_show_dict)
            return_data.append(coupon_dict)
    
            return return_data
    
        def get(self,request):
            '''获取redis中当前登录用户的所有的待支付信息'''
            ret = StatusRet()
            return_data = self.get_return_data(request)
    
            ret.data = return_data
    
            return Response(ret.dict)
    
        def post(self,request):
            '''将用户选择的商品信息存储到redis中'''
            ret = StatusRet()
    
            # 获取前端发送的所有选中的数据
            course_id_list = request.data.get('course_id')
            for course_id in course_id_list:
                shop_car_key = settings.SHOP_CAR_COURSE_KEY%(request.user.pk,course_id)
                # 校验数据是不是在购物车中
                if not self.conn.exists(shop_car_key):
                    ret.code = 2000
                    ret.msg = '课程不存在'
                    return Response(ret.dict)
                else:
                    # 校验通过后  获取当前的时间
                    now = datetime.datetime.now()
                    # 获取选中课程在购物车中的详细信息
                    course_price_data = self.conn.hgetall(shop_car_key)
                    # 将取出的数据进行重新构建
                    course_detail = {}
                    for key,val in course_price_data.items():
                        if key == b'price_policy':
                            course_detail[key.decode()] = json.loads(val.decode())
                        else:
                            course_detail[key.decode()]=val.decode()
                    # 构建存进redis中的数据
                    coupon_dict = {}
                    common_dict = {}
                    # 设置全局的存储name的格式
                    pay_course_settings = settings.PAYMENT_COURSE_KEY
                    payment_key =pay_course_settings%(request.user.pk,course_id)
                    # 获取当前用户在优惠券领取记录表中所有的未使用,并且未过期的优惠券
                    couponrecord_list = models.CouponRecord.objects.filter(user=request.user,status=0,coupon__valid_begin_date__lte=now,coupon__valid_end_date__gte=now,number__gte=1,coupon__object_id=course_id)
    
                    for coupon_record_obj in couponrecord_list:
                        coupon_obj = coupon_record_obj.coupon
                        temp = {
                            'name':coupon_obj.name,
                            'coupon_type':coupon_obj.coupon_type,
                            'money_equivalent_value':coupon_obj.money_equivalent_value,
                            'off_percent':coupon_obj.off_percent,
                            'minimum_consume':coupon_obj.minimum_consume,
                            'object_id':coupon_obj.object_id
                        }
                        # 判断是通用优惠券还是某个课程的专用优惠券
                        if not coupon_obj.object_id:
                            common_dict[coupon_record_obj.pk] = json.dumps(temp,ensure_ascii=False)
                        else:
                            coupon_dict[coupon_record_obj.pk] = json.dumps(temp,ensure_ascii=False)
                    val_dict = {}
                    val_dict['course_detail'] = json.dumps(course_detail,ensure_ascii=False)
    
                    val_dict['coupons'] = json.dumps(coupon_dict,ensure_ascii=False)
                    self.conn.hmset(payment_key,val_dict)
                    common_settint_key = settings.PAYMENT_COMMON_KEY
                    common_key = common_settint_key%(request.user.pk)
                    # 如果没有通用优惠券,在存储redis时会报错,因为存储的value不能为一个空字典
                    if common_dict:
                        self.conn.hmset(common_key,common_dict)
            #
            # 获取当前用户的贝里数
            balance = request.user.beli_count
            balance_dict = {'balance': balance}
            user_balance_key = settings.USER_BALANCE_COUNT
            user_balance_count = user_balance_key%request.user.pk
            self.conn.hmset(user_balance_count,balance_dict)
    
            # 获取返回前端的数据
            return_data = self.get_return_data(request)
            ret.data = return_data
    
            return Response(ret.dict)

    支付页面

    结算完毕就该支付页面了,用户点击支付后,会将所有要支付的数据发回来,进行数据的校验,校验通过后,生成order订单表(或订单详情表),然后就可以调用支付的接口

    1.url的构建

    url(r'^payment/',views.PaymentViewSet.as_view()),

    2.逻辑的实现

      数据取出可以是从redis中取,也可以从数据库中取,进行校验,具体的实现粒度视情况

    class PaymentViewSet(APIView):
        authentication_classes = [LoginAuth]
        conn = get_redis_connection('default')
        now = datetime.datetime.now()
    
        def post(self,request):
            '''
            前端发送的数据结构:
            {
                "money":87.12,  # 总价格
                "balance":0,    # 贝里数
                "course_detail":  #课程详细   包含该课程选中的优惠券pk值,价格id,价格
                    {
                        "1":{"coupon_id":1,"policy_id":1,"price":99},
                        "2":{"coupon_id":5,"policy_id":4,"price":0}
                    },
                "common_coupon_id":4   # 通用优惠券的pk值
            }
            
            '''
            #获取数据
            ret = StatusRet()
    
            user_id = request.user.pk
            _balance = models.UserInfo.objects.filter(pk=user_id).first().beli_count
            course_detail = request.data.get("course_detail")
            money = request.data.get('money')
            currency_coupon_id = request.data.get('common_coupon_id')
            balance = request.data.get('balance')
            # 数据处理
            try:
                if balance>_balance:
                    raise OrderError('贝利数异常,支付失败')
                course_price_list = []
                for course_id,course_info in course_detail.items():
                    # 从redis中购物车中取数据校验
                    # pay_key = settings.PAYMENT_COURSE_KEY%(user_id,course_id)
                    # pay_ret = self.conn.hgetall(pay_key)
                    # print(pay_ret,'pay_key')
                    # for key,value in pay_ret.items():
                    #     # key = key.decode()
                    #     value = json.loads(value.decode())
                    #     print(key,value)
                    #     if key == b'coupons':
                    #         if course_info['coupon_id'] not in value:
                    #             raise OrderError('优惠券不存在')
                    #     elif key == b'course_detail':
                    #         for key_course,value_course in value.items():
                    #             if key_course == 'title':
                    #                 pass
                    #             elif key_course == 'checked_price_policy_id':
                    #                 if value_course != str(course_info['policy_id']):
                    #                     raise OrderError('选中的价格策略异常')
                    #             elif key_course == 'price_policy':
                    #                 # print(value_course,type(value_course))
                    #                 course_price = value_course[str(course_info['policy_id'])]['price']
                    #                 if course_price != course_info['price']:
                    #                     raise OrderError('课程价格异常')
                    #                 course_price_list.append(course_price)
    
                                    
                    # 数据库中取数据校验
                    course_obj = models.Courses.objects.filter(pk=course_id).first()
                    if not course_obj:
                        raise OrderError('课程不存在')
                    if course_obj.status !=0:
                        raise OrderError('课程已下架')
                    policy_list = list(course_obj.price_policy.all().values('pk','price'))
                    # print(type(course_info['policy_id']))
                    if course_info['policy_id'] not in [i['pk'] for i in policy_list]:
                        raise OrderError('价格策略不存在')
                    print(policy_list, '价格策略列表')
                    print([float(i['price']) for i in policy_list if i['pk'] ==course_info['policy_id'] ])
                    policy_price = [float(i['price']) for i in policy_list if i['pk'] ==course_info['policy_id']][0]
                    if policy_price != course_info['price']:
                        raise OrderError('价格异常')
    
                    # 更细粒度的校验实现
                    # xxx = models.CouponRecord.objects.filter(pk=course_info['coupon_id'],user_id=user_id).first()
                    # if not xxx:
                    #     raise OrderError('优惠券不存在')
                    # if xxx.status !=0:
                    #     raise OrderError('优惠券未上架')
                    # if int(xxx.number) <1:
                    #     raise OrderError('优惠券数量不够')
                    #  ...
    
                    if course_info['coupon_id']:
                        coupon_obj = models.CouponRecord.objects.filter(pk=course_info['coupon_id'],user_id=user_id,status=0,number__gte =1,coupon__valid_begin_date__lte=self.now,coupon__valid_end_date__gte=self.now,coupon__object_id=course_id).first()
                        if not coupon_obj:
                            raise OrderError('优惠券异常,支付失败')
                    # 获取每个课程的价格
                        price_course = self.account_price(coupon_obj,policy_price)
                    else:
                        price_course = policy_price
                
                    course_price_list.append(price_course)
               
                # 获取价格总和
                course_price_total = sum(course_price_list)
                # 通用优惠券的校验和使用
                if currency_coupon_id:
                    currency_coupon_obj = models.CouponRecord.objects.filter(pk=currency_coupon_id,user_id=user_id,status=0,number__gte =1,coupon__valid_begin_date__lte=self.now,coupon__valid_end_date__gte=self.now).first()
                    if not currency_coupon_obj:
                        raise OrderError('通用优惠卷异常')
                    course_total = self.account_price(currency_coupon_obj,course_price_total)
                else:
                    course_total = course_price_total
                # 贝里数的扣减
                if balance>0:
                    _finally_price = course_total- balance/100
                else:
                    _finally_price = course_total
                finally_price = round(_finally_price,2)
                # 总价格的校验
                if finally_price != money:
                    raise OrderError('总价格异常')
    
                # 生成订单信息
    
                order_obj = models.Order.objects.create(payment_type=1,order_number=self.get_random_order(),actual_amount=finally_price,user_id=user_id,status=1)
                # models.OrderDetail.objects.create(order=order_obj,original_price=course_price_total,price=finally_price,valid_period_display='课程有效期',valid_period=100,content_type=)
    
                # 调用支付接口
                # xxxxxxxxxxxxxxxxxxxxx
    
            except OrderError as e:
                ret.code = 2000
                ret.msg = str(e)
    
            return Response(ret.dict)
    
        def account_price(self, coupon_record_obj, price):
            """
            根据优惠券记录对象,以及优惠前的价格 计算出使用优惠券的价格
            """
            coupon_type = coupon_record_obj.coupon.coupon_type
            if coupon_type == 0:
                money_equivalent_value = coupon_record_obj.coupon.money_equivalent_value
                # --立减
                # 0 = 获取原价 - 优惠券金额   或
                # 折后价格 = 获取原价 - 优惠券金额
                if price - money_equivalent_value >= 0:
                    rebate_price = price - money_equivalent_value
                else:
                    rebate_price = 0
            elif coupon_type == 1:
                # -- 满减 是否满足限制
                minimum_consume = coupon_record_obj.coupon.minimum_consume
                if price >= minimum_consume:
                    # 折后价格 = 获取原价 - 优惠券金额
                    money_equivalent_value = coupon_record_obj.coupon.money_equivalent_value
                    rebate_price = price - money_equivalent_value
                else:
                    return price
            elif coupon_type == 2:
                # -- 折扣
                off_percent = coupon_record_obj.coupon.off_percent
                # 折后价格 = 获取原价* 80/100
                rebate_price = price * (off_percent / 100)
            else:
                rebate_price = price
            return rebate_price
    
        def get_random_order(self):
            '''生成随机订单号'''
            import random
            time_current = self.now.strftime('%Y%m%d%H%M%S')
            random_str = ''.join([str(random.randint(0,9)) for i in range(4)])
            return f'{time_current}{random_str}'

    附表信息:

    from datetime import datetime
    from django.db import models
    
    from django.contrib.contenttypes.models import ContentType
    from django.contrib.contenttypes.fields import GenericRelation,GenericForeignKey
    
    
    # Create your models here.
    
    
    
    class Courses(models.Model):
        title = models.CharField(max_length=64)
        price = models.DecimalField(max_digits=8,decimal_places=2)
        classes = ((1,'初级'),(2,'中级'),(3,'高级'))
        course_class = models.SmallIntegerField(choices=classes)
        course_img = models.CharField(max_length=128)
        status_choices = ((0, '上线'), (1, '下线'), (2, '预上线'))
        status = models.SmallIntegerField(choices=status_choices, default=0,help_text='课程的状态')
    
        price_policy = GenericRelation(to='PricePolicy')
    
        questions = GenericRelation(to='OftenQuestion')
    
        coupons = GenericRelation(to='Coupon')
    
        def __str__(self):
            return self.title
    
    
    class CourseDetail(models.Model):
        course = models.OneToOneField(to='Courses')
        slogen = models.CharField(max_length=128)
        desc = models.CharField(max_length=512)
        hours = models.IntegerField('课时',default=10)
        recommend_courses = models.ManyToManyField(to='Courses',related_name="recourse")
    
        def __str__(self):
            return self.course.title
    
    
    class ChapterList(models.Model):
        '''课程章节'''
        num = models.SmallIntegerField()
        name = models.CharField(max_length=64)
        course = models.ForeignKey(to='Courses',on_delete=models.CASCADE)
    
        def __str__(self):
            return "%s:(第%s章)%s"%(self.course,self.num,self.name)
    
        class Meta:
            unique_together = ('num','course')
    
    
    class CourseSection(models.Model):
        '''课时目录'''
        name = models.CharField(max_length=64)
        charpter = models.ForeignKey(to='ChapterList')
    
        def __str__(self):
            return self.name
    
    
    class UserInfo(models.Model):
        name = models.CharField(max_length=16)
        password = models.CharField(max_length=32)
    
        beli_count = models.IntegerField(default=0)
    
    
        def __str__(self):
            return self.name
    
    
    class Token(models.Model):
        name = models.CharField(max_length=128)
        user = models.OneToOneField(to='UserInfo')
    
        def __str__(self):
            return self.name
    
    
    class PricePolicy(models.Model):
        '''价格策略表'''
    
        name = models.CharField(max_length=128)
        price = models.DecimalField(max_digits=6,decimal_places=2,default=199)
    
        content_type = models.ForeignKey(to=ContentType)
        object_id = models.PositiveIntegerField()
        content_obj = GenericForeignKey('content_type','object_id')
    
        def __str__(self):
            return self.name
    
        class Meta:
            unique_together = ('content_type','object_id','name')
    
    
    class OftenQuestion(models.Model):
        question = models.CharField(max_length=128)
        answer = models.TextField()
    
        content_type = models.ForeignKey(to=ContentType)
        object_id = models.PositiveIntegerField()
        content_obj = GenericForeignKey('content_type','object_id')
    
        def __str__(self):
            return self.question
    
        class Meta:
            unique_together = ('content_type','object_id','question')
    
    
    class Coupon(models.Model):
        """优惠券生成规则"""
        name = models.CharField(max_length=64, verbose_name="活动名称")
        brief = models.TextField(blank=True, null=True, verbose_name="优惠券介绍")
        coupon_type_choices = ((0, '通用券'), (1, '满减券'), (2, '折扣券'))
        coupon_type = models.SmallIntegerField(choices=coupon_type_choices, default=0, verbose_name="券类型")
    
        money_equivalent_value = models.IntegerField(verbose_name="等值货币",blank=True,null=True)
        off_percent = models.PositiveSmallIntegerField("折扣百分比", help_text="只针对折扣券,例7.9折,写79", blank=True, null=True)
        minimum_consume = models.PositiveIntegerField("最低消费", default=0, help_text="仅在满减券时填写此字段",blank=True,null=True)
    
        content_type = models.ForeignKey(ContentType, blank=True, null=True)
        object_id = models.PositiveIntegerField("绑定课程", blank=True, null=True, help_text="可以把优惠券跟课程绑定")
        content_object = GenericForeignKey('content_type', 'object_id')
    
        quantity = models.PositiveIntegerField("数量(张)", default=1)
        open_date = models.DateField("优惠券领取开始时间")
        close_date = models.DateField("优惠券领取结束时间")
        valid_begin_date = models.DateField(verbose_name="有效期开始时间", blank=True, null=True)
        valid_end_date = models.DateField(verbose_name="有效结束时间", blank=True, null=True)
        coupon_valid_days = models.PositiveIntegerField(verbose_name="优惠券有效期(天)", blank=True, null=True,
                                                        help_text="自券被领时开始算起")
        date = models.DateTimeField(auto_now_add=True)
    
        class Meta:
            verbose_name_plural = "31. 优惠券生成记录"
    
        def __str__(self):
            return "%s(%s)" % (self.get_coupon_type_display(), self.name)
    
        def save(self, *args, **kwargs):
            if not self.coupon_valid_days or (self.valid_begin_date and self.valid_end_date):
                if self.valid_begin_date and self.valid_end_date:
                    if self.valid_end_date <= self.valid_begin_date:
                        raise ValueError("valid_end_date 有效期结束日期必须晚于 valid_begin_date ")
                if self.coupon_valid_days == 0:
                    raise ValueError("coupon_valid_days 有效期不能为0")
            if self.close_date < self.open_date:
                raise ValueError("close_date 优惠券领取结束时间必须晚于 open_date优惠券领取开始时间 ")
    
            super(Coupon, self).save(*args, **kwargs)
    
    
    class CouponRecord(models.Model):
        """优惠券发放、消费纪录"""
        coupon = models.ForeignKey("Coupon")
        number = models.CharField(max_length=64, unique=True)
        user = models.ForeignKey("UserInfo", verbose_name="拥有者")
        status_choices = ((0, '未使用'), (1, '已使用'), (2, '已过期'))
        status = models.SmallIntegerField(choices=status_choices, default=0)
        get_time = models.DateTimeField(verbose_name="领取时间", help_text="用户领取时间")
        used_time = models.DateTimeField(blank=True, null=True, verbose_name="使用时间")
        order = models.ForeignKey("Order", blank=True, null=True, verbose_name="关联订单")  # 一个订单可以有多个优惠券
    
        class Meta:
            verbose_name_plural = "32. 用户优惠券"
    
        def __str__(self):
            return '%s-%s-%s' % (self.user, self.number, self.status)
    
    
    class Order(models.Model):
        """订单"""
        payment_type_choices = ((0, '微信'), (1, '支付宝'), (2, '优惠码'), (3, '贝里'))
        payment_type = models.SmallIntegerField(choices=payment_type_choices)
        payment_number = models.CharField(max_length=128, verbose_name="支付第3方订单号", null=True, blank=True)
        order_number = models.CharField(max_length=128, verbose_name="订单号", unique=True)  # 考虑到订单合并支付的问题
        user = models.ForeignKey("UserInfo")
        actual_amount = models.FloatField(verbose_name="实付金额")
    
        status_choices = ((0, '交易成功'), (1, '待支付'), (2, '退费申请中'), (3, '已退费'), (4, '主动取消'), (5, '超时取消'))
        status = models.SmallIntegerField(choices=status_choices, verbose_name="状态")
        date = models.DateTimeField(auto_now_add=True, verbose_name="订单生成时间")
        pay_time = models.DateTimeField(blank=True, null=True, verbose_name="付款时间")
        cancel_time = models.DateTimeField(blank=True, null=True, verbose_name="订单取消时间")
    
        class Meta:
            verbose_name_plural = "37. 订单表"
    
        def __str__(self):
            return "%s" % self.order_number
    
    
    class OrderDetail(models.Model):
        """订单详情"""
        order = models.ForeignKey("Order")
    
    
        content_type = models.ForeignKey(ContentType)  # 可关联普通课程或学位
        object_id = models.PositiveIntegerField()
        content_object = GenericForeignKey('content_type', 'object_id')
    
        original_price = models.FloatField("课程原价")
        price = models.FloatField("折后价格")
        content = models.CharField(max_length=255, blank=True, null=True)  #
        valid_period_display = models.CharField("有效期显示", max_length=32)  # 在订单页显示
        valid_period = models.PositiveIntegerField("有效期(days)")  # 课程有效期
        memo = models.CharField(max_length=255, blank=True, null=True)
    
        def __str__(self):
            return "%s - %s - %s" % (self.order, self.content_type, self.price)
    
        class Meta:
            verbose_name_plural = "38. 订单详细"
            unique_together = ("order", 'content_type', 'object_id')
    models.py
  • 相关阅读:
    python2将str类型与unicode类型字符串写入文件的编码问题
    正则表达式匹配邮箱
    python读取excel表格
    python正则匹配re.search与re.findall的区别
    python使用join提取列表类型中的数字字符串
    判断一个python字符串中是否包含中文字符
    wordstation中虚拟机关机hang住,无法正常关机
    网络中涉及到优先级的主设备会被抢占与非抢占。
    [转]一天一点学习Linux之Inode详解
    [转]理解Linux文件系统之inode(非ext格式)
  • 原文地址:https://www.cnblogs.com/zhaopanpan/p/9295835.html
Copyright © 2020-2023  润新知