1. Shopping cart validity switch
2. Switch the price according to the validity period
3. Shopping cart delete operation
5. Order page - initialization
1.course/models.py
class CourseExpire(BaseModel): """ Course validity model """ # You can put it in the database later course and expire_time Field set to Federated index course = models.ForeignKey("Course", related_name='course_expire', on_delete=models.CASCADE, verbose_name=" Course name ") # Term of validity , Days expire_time = models.IntegerField(verbose_name=" The period of validity ", null=True, blank=True, help_text=" The validity period is calculated in days ") # One month, and so on expire_text = models.CharField(max_length=150, verbose_name=" Tip text ", null=True, blank=True) # Price per validity period price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name=" Price of course ", default=0) class Meta: db_table = "ly_course_expire" verbose_name = " Course validity " verbose_name_plural = verbose_name def __str__(self): return " Course :%s, The period of validity :%s, Price :%s" % (self.course, self.expire_text, self.price)
stay course In the table price Field with a hint text
price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name=" Course the original price ", default=0,help_text=' If the price is 0, So it means that when the current course is purchased , There is no permanent term of validity .')
2. Insert some test data
INSERT INTO `ly_course_expire` (`id`,`orders`,`is_show`,`is_deleted`,`created_time`,`updated_time`,`expire_time`,`expire_text`,`course_id`,`price`) VALUES (1,1,1,0,'2019-08-19 02:05:22.368823','2019-08-19 02:05:22.368855',30,' For one month ',1,398.00), (2,2,1,0,'2019-08-19 02:05:37.397205','2019-08-19 02:05:37.397233',60,'2 Months in effect ',1,588.00), (3,3,1,0,'2019-08-19 02:05:57.029411','2019-08-19 02:05:57.029440',180,' Within half a year ',1,1000.00), (4,4,1,0,'2019-08-19 02:07:29.066617','2019-08-19 02:08:29.156730',3,'3 Within days ',3,0.88), (5,3,1,0,'2019-08-19 02:07:46.120827','2019-08-19 02:08:18.652452',30,'1 Months in effect ',3,188.00), (6,3,1,0,'2019-08-19 02:07:59.876421','2019-08-19 02:07:59.876454',60,'2 Months in effect ',3,298.00);
3.xadmin register
course/adminx.py
from .models import CourseExpire class CourseExpireModelAdmin(object): """ Commodity validity model """ pass xadmin.site.register(CourseExpire, CourseExpireModelAdmin)
course/models.py
class Course: # Get course validity def get_expire(self): # The one to many relationship between the course schedule and the course validity schedule Reverse query expire_list = self.course_expire.all() # Query the validity type of the current course data = [] for expire in expire_list: data.append({ 'id':expire.id, 'expire_text':expire.expire_text, # The text of the validity period such as ' Three months ' 'price':expire.price, # The price corresponding to the validity period }) # When the price is 0 when , There's no permanent term , Everything else has if self.price > 0: data.append({ 'id': 0, 'expire_text': ' Permanent validity ', 'price': self.price, }) return data
cart/views.py
class AddCartView(ViewSet): def cart_list(self,request): try: ...... cart_data_list.append({ ...... 'expire_list':course_obj.get_expire(), ...... }) except Exception: ...... return Response({'msg':'xxx','cart_data_list':cart_data_list})
cartitem.vue
<div class="cart_column column_3"> <el-select v-model="cart.expire_id" size="mini" placeholder=" Please select the purchase validity period " class="my_el_select"> <el-option :label="expire.expire_text" :value="expire.id" :key="expire.id" v-for="(expire,expire_index) in cart.expire_list"></el-option> </el-select> </div>
cart/views.py
class AddCartView: def change_expire(self,request): user_id = request.user.id course_id = request.data.get('course_id') expire_id = request.data.get('expire_id') # Testing course id Whether it works try: course_obj = models.Course.objects.get(id=course_id) except: return Response({'msg':' The curriculum doesn't exist '},status=status.HTTP_400_BAD_REQUEST) # Inspection validity period id Whether it works try: if expire_id > 0: expire_object = models.CourseExpire.objects.get(id=expire_id) except: return Response({'msg':' The course validity period does not exist '},status=status.HTTP_400_BAD_REQUEST) # Calculation xx Course xx The real price of the validity period real_price = course_obj.real_price(expire_id) # Changed the new validity period , To save the validity period after the change to redis in conn = get_redis_connection('cart') conn.hset('cart_%s' % user_id, course_id, expire_id) return Response({'msg':' Switch successful !', 'real_price': real_price})
cart/urls.py
urlpatterns = [ ...... path('expires/', views.AddCartView.as_view({'put':'change_expire'})) ]
class Course: def real_price(self,expire_id=0): # increase expire_id=0 Parameters Let the real price display different prices according to different validity periods price = float(self.price) ''' 1. If it's not permanent (expire_id>0), Then from course_expire Get the price corresponding to the validity period 2. If it's permanent (expire_id=0), Then the real price is equal to its original price ''' if expire_id > 0: expire_obj = self.course_expire.get(id=expire_id) price = float(expire_obj.price) r_price = price a = self.activity() if a: sale = a[0].discount.sale condition_price = a[0].discount.condition # Time limited free if not sale.strip(): r_price = 0 # Limited time discount *0.5 elif '*' in sale.strip(): if price >= condition_price: _, d = sale.split('*') r_price = price * float(d) # Time limited relief -100 elif sale.strip().startswith('-'): if price >= condition_price: _, d = sale.split('-') r_price = price - float(d) elif ' full ' in sale: if price >= condition_price: l1 = sale.split('\r\n') dis_list = [] #10 50 25 for i in l1: a, b = i[1:].split('-') #400 if price >= float(a): dis_list.append(float(b)) max_dis = max(dis_list) r_price = price - max_dis return r_price
cartitem.vue
// js watch:{ ...... // When the validity period of the course selected by the user changes ( Select another validity period in the drop-down box at the front end ) 'cart.expire_id':function (){ let token = localStorage.token || sessionStorage.token; this.$axios.put(`${this.$settings.Host}/cart/expires/`,{ // Will the course id The validity period corresponding to the course id Commit to the backend course_id: this.cart.course_id, expire_id:this.cart.expire_id, },{ headers:{ 'Authorization':'jwt ' + token } } ).then((res)=>{ this.$message.success(res.data.msg); // Modification of validity period succeeded , Return the real price to the front end this.cart.real_price = res.data.real_price; // Modification of validity period succeeded , Trigger cart Event that the parent component recalculates the total price this.$emit('change_expire_handler',) }).catch((error)=>{ this.$message.error(error.response.data.msg) }) } },
cart.vue
<div class="cart_course_list"> <CartItem v-for="(value,index) in cart_data_list" :key="index" :cart="value" @cal_t_p="cal_total_price" @change_expire_handler="cal_total_price" @delete_course_handler="delete_c(index)"></CartItem> </div>
cart/views.py
class AddCartView: def cart_list(self,request): user_id = request.user.id conn = get_redis_connection('cart') conn.delete('selected_cart_%s' % user_id) ret = conn.hgetall('cart_%s' % user_id) #dict {b'1': b'0', b'2': b'0'} cart_data_list = [] try: for cid, eid in ret.items(): course_id = cid.decode('utf-8') # When the user looks at the shopping cart page -> The default display is permanent conn.hset('cart_%s' % user_id, course_id, 0) expire_id = 0 course_obj = models.Course.objects.get(id=course_id) cart_data_list.append({ 'course_id':course_obj.id, 'name':course_obj.name, 'course_img':contains.SERVER_ADDR + course_obj.course_img.url , 'price':course_obj.price, 'real_price':course_obj.real_price(), 'expire_id':expire_id, 'expire_list':course_obj.get_expire(), 'selected':False, # By default, it is not checked }) except Exception: logger.error(' Failed to get cart data ') return Response({'msg':' There's something wrong with the background database , Please contact the Administrator '},status=status.HTTP_507_INSUFFICIENT_STORAGE) return Response({'msg':'xxx','cart_data_list':cart_data_list})
<div class="cart_column column_4" id="delete" @click="delete_course"> Delete </div>
delete_course(){ let token = localStorage.token || sessionStorage.token; this.$axios.delete(`${this.$settings.Host}/cart/add_cart/`,{ params:{ course_id: this.cart.course_id, // request.query_params.get('course_id') }, headers:{ 'Authorization':'jwt ' + token } }) .then((res)=>{ this.$message.success(res.data.msg); // When a user wants to delete a course record in the shopping cart perform Cart Delete course event of parent component this.$emit('delete_course_handler') }) .catch((error)=>{ this.$message.error(error.response.data.msg); }) }
urlpatterns = [ path('add_cart/', views.AddCartView.as_view( {'post':'add','get':'cart_list', 'patch':'change_select','put':'cancel_select', 'delete':'delete_course' })), path('expires/', views.AddCartView.as_view({'put':'change_expire'})) ]
cart/views.py
class AddCartView: def delete_course(self,request): user_id = request.user.id course_id = request.query_params.get('course_id') conn = get_redis_connection('cart') pipe = conn.pipeline() pipe.hdel('cart_%s' % user_id, course_id) pipe.srem('selected_cart_%s' % user_id, course_id) pipe.execute() return Response({'msg':' Delete successful '})
User delete a shopping cart data The back end has been removed But the front page should also be deleted synchronously
Cart.vue
<div class="cart_course_list"> <CartItem v-for="(value,index) in cart_data_list" :key="index" :cart="value" @cal_t_p="cal_total_price" @change_expire_handler="cal_total_price" @delete_course_handler="delete_c(index)"></CartItem> </div>
delete_c(index){ this.cart_data_list.splice(index,1) this.cal_total_price() // After deleting Re trigger the method of calculating the total price }
Cartitem.vue
delete_course(){ let token = localStorage.token || sessionStorage.token; this.$axios.delete(`${this.$settings.Host}/cart/add_cart/`,{ params:{ course_id: this.cart.course_id, }, headers:{ 'Authorization':'jwt ' + token } }) .then((res)=>{ // deleted Trigger Cart Delete event of parent component this.$message.success(res.data.msg); this.$emit('delete_course_handler') }) .catch((error)=>{ this.$message.error(error.response.data.msg); }) } }
1. Settlement initial page
<!-- Initial interface of settlement page -->
2. Configure the routing
index.js
import Vue from 'vue' import Order from "@/components/Order" Vue.use(Router) export default new Router({ mode:'history', routes: [ ...... { path: '/order/', component: Order }, ] })
3. Click the go to checkout button See the page
cart.vue
<span class="goto_pay"><router-link to="/order/"> To settle accounts </router-link></span>
cart/urls.py
urlpatterns = [ ... path('expires/', views.AddCartView.as_view({'get':'show_pay_info'})) ]
cart/views.py
def show_pay_info(self,request): user_id = request.user.id conn = get_redis_connection('cart') # Get all courses selected by the user's shopping cart id select_list = conn.smembers('selected_cart_%s' % user_id) data = [] # Get user shopping cart course id: The period of validity id ret = conn.hgetall('cart_%s' % user_id) # dict {b'1': b'0', b'2': b'0'} for cid, eid in ret.items(): expire_id = int(eid.decode('utf-8')) if cid in select_list: # If the course is checked course_id = int(cid.decode('utf-8')) # Access to this ' Selected courses 'model object course_obj = models.Course.objects.get(id=course_id) # If the validity period of the course is not permanent if expire_id > 0: expire_obj = models.CourseExpire.objects.get(id=expire_id) data.append({ 'course_id':course_obj.id, 'name':course_obj.name, 'course_img':contains.SERVER_ADDR + course_obj.course_img.url , 'real_price':course_obj.real_price(expire_id), 'expire_text':expire_obj.expire_text, }) # If the course is valid for a permanent period else: data.append({ 'course_id': course_obj.id, 'name': course_obj.name, 'course_img': contains.SERVER_ADDR + course_obj.course_img.url, 'real_price': course_obj.real_price(expire_id), 'expire_text': ' Permanent validity ', }) return Response({'data':data})
order.vue
// js methods: { get_order_data(){ let token = localStorage.token || sessionStorage.token; this.$axios.get(`${this.$settings.Host}/cart/expires/`,{ headers:{ 'Authorization':'jwt ' + token } }).then((res)=>{ this.course_list = res.data.data; }) },
<!-- html --> <div class="cart-item" v-for="(course,index) in course_list"> <el-row> <el-col :span="2" class="checkbox"> </el-col> <el-col :span="10" class="course-info"> <img :src="course.course_img" alt=""> <span>{{course.name}}</span> </el-col> <el-col :span="8"><span>{{course.expire_text}}</span></el-col> <el-col :span="4" class="course-price">¥{{course.real_price}}</el-col> </el-row> </div>
<el-col :span="8"> <span class="alipay" v-if="pay_type===1"><img src="../../static/img/alipay2.png" alt=""></span> <span class="alipay" @click="pay_type=1" v-else><img src="../../static/img/alipay.png" alt=""></span> <span class="alipay wechat" v-if="pay_type===2"><img src="../../static/img/wechat2.png" alt="" ></span> <span class="alipay wechat" @click="pay_type=2" v-else><img src="../../static/img/wechat.png" alt=""></span> </el-col>
from django.db import models # Create your models here. from lyapi.utils.models import BaseModel from users.models import User from course.models import Course class Order(BaseModel): """ Order model """ status_choices = ( (0, ' Did not pay '), (1, ' Paid '), (2, ' Cancelled '), (3, ' Time out cancellation '), ) pay_choices = ( (0, ' Alipay '), (1, ' Wechat payment '), ) order_title = models.CharField(max_length=150,verbose_name=" Order title ") total_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name=" The order price ", default=0) real_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name=" Amount paid ", default=0) order_number = models.CharField(max_length=64,verbose_name=" The order number ") order_status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name=" The order status ") pay_type = models.SmallIntegerField(choices=pay_choices, default=1, verbose_name=" Method of payment ") credit = models.IntegerField(default=0, verbose_name=" The number of points used ") coupon = models.IntegerField(null=True, verbose_name=" User coupons ID") order_desc = models.TextField(max_length=500, verbose_name=" Order description ",null=True,blank=True) pay_time = models.DateTimeField(null=True, verbose_name=" Time of payment ") user = models.ForeignKey(User, related_name='user_orders', on_delete=models.DO_NOTHING,verbose_name=" Order user ") class Meta: db_table="ly_order" verbose_name= " Order records " verbose_name_plural= " Order records " def __str__(self): return "%s, The total price : %s, Paid in : %s" % (self.order_title, self.total_price, self.real_price) class OrderDetail(BaseModel): """ Order details """ order = models.ForeignKey(Order, related_name='order_courses', on_delete=models.CASCADE, verbose_name=" Order ID") course = models.ForeignKey(Course, related_name='course_orders', on_delete=models.CASCADE, verbose_name=" Course ID") expire = models.IntegerField(default='0', verbose_name=" Validity period ",help_text="0 Permanent ") price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name=" Course the original price ") real_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name=" The course is real ") discount_name = models.CharField(max_length=120,default="",verbose_name=" Type of offer ") class Meta: db_table="ly_order_detail" verbose_name= " Order details " verbose_name_plural= " Order details " def __str__(self): return "%s" % (self.course.name)
order/adminx.py
import xadmin from .models import Order class OrderModelAdmin(object): """ Order model management class """ pass xadmin.site.register(Order, OrderModelAdmin) from .models import OrderDetail class OrderDetailModelAdmin(object): """ Order detail model management class """ pass xadmin.site.register(OrderDetail, OrderDetailModelAdmin)
default_app_config = "order.apps.OrderConfig"
order/app.py
from django.apps import AppConfig class OrderConfig(AppConfig): name = 'order' verbose_name = ' Order management '
order/urls.py
from django.urls import path,re_path from . import views urlpatterns = [ path('add_money/',views.OrderView.as_view(),) ]
order/views.py
from django.shortcuts import render from rest_framework.generics import CreateAPIView # Create your views here. from . import models from .serializers import OrderModelSerializer from rest_framework.permissions import IsAuthenticated class OrderView(CreateAPIView): queryset = models.Order.objects.filter(is_deleted=False,is_show=True) serializer_class = OrderModelSerializer permission_classes = [IsAuthenticated, ]
order/serializers.py
import datetime from rest_framework import serializers from . import models from django_redis import get_redis_connection from course.models import Course from course.models import CourseExpire from django.db import transaction class OrderModelSerializer(serializers.ModelSerializer): class Meta: model = models.Order fields = ['id', 'order_number', 'pay_type', 'coupon', 'credit'] ''' 1. The user needs to submit the data in the order page : Payment type : WeChat / Alipay Coupon integral 2. After the user submits the data successfully , The front page needs to return the data : id The order number ''' extra_kwargs = { 'id':{'read_only':True}, 'order_number':{'read_only':True}, 'pay_type':{'write_only':True}, 'coupon':{'write_only':True}, 'credit':{'write_only':True}, } def validate(self, attrs): # Get the payment method used by the user pay_type = int(attrs.get('pay_type',0)) # # Verify whether the payment method used by users is one of Alipay or WeChat if pay_type not in [i[0] for i in models.Order.pay_choices]: raise serializers.ValidationError(' The payment method is wrong !') # todo Coupon verification , See if it's overdue, etc # todo Integral upper limit check return attrs def create(self, validated_data): try: # Generate order number [ date , user id, Self increasing data ] current_time = datetime.datetime.now() now = current_time.strftime('%Y%m%d%H%M%S') user_id = self.context['request'].user.id conn = get_redis_connection('cart') num = conn.incr('num') # Generate a unique order number order_number = now + "%06d" % user_id + "%06d" % num with transaction.atomic(): # Add transaction # Generate order order_obj = models.Order.objects.create(**{ 'order_title': '31 Time order ', 'total_price': 0, 'real_price': 0, 'order_number': order_number, 'order_status': 0, 'pay_type': validated_data.get('pay_type', 0), 'credit': 0, 'coupon': 0, 'order_desc': ' Girl friend ', 'pay_time': current_time, 'user_id': user_id, # 'user':user_obj, }) select_list = conn.smembers('selected_cart_%s' % user_id) ret = conn.hgetall('cart_%s' % user_id) # dict {b'1': b'0', b'2': b'0'} for cid, eid in ret.items(): expire_id = int(eid.decode('utf-8')) if cid in select_list: course_id = int(cid.decode('utf-8')) course_obj = Course.objects.get(id=course_id) if expire_id > 0: expire_text = CourseExpire.objects.get(id=expire_id).expire_text # Generate order details models.OrderDetail.objects.create(**{ 'order': order_obj, 'course': course_obj, 'expire': expire_id, 'price': course_obj.price, 'real_price': course_obj.real_price(expire_id), 'discount_name': course_obj.discount_name(), }) # print('xxxxx') except Exception: raise models.Order.DoesNotExist return order_obj