Catalog
1. Course list page activity and real price calculation
1. Preferential campaign strategy model Table structure
2. The course list page shows the name of the offer type
3. The course list page shows the real price
4. Display the name of the offer type and the real price on the front page
5. The course list page shows the specific end time
2. Add cart / Check the user authentication of the shopping cart
3. Check the price of shopping cart / Settlement
1. The real price of each course is displayed on the shopping cart page
2. Check / Non check should be in redis In real time storage - Back end interface
3. Check / If not, the price should be recalculated in the front page
4. Shopping cart list shows - Back end interface
1. Course list page activity and real price calculation
Since we mentioned the event , It must be a preferential strategy 、 Promotional activities and so on .
So we need to create a separate table structure for the promotion strategy
1. Preferential campaign strategy model Table structure
class CourseDiscountType(BaseModel): """ Course offer type """ name = models.CharField(max_length=32, verbose_name=" Offer type name ") remark = models.CharField(max_length=250, blank=True, null=True, verbose_name=" Notes ") class Meta: db_table = "ly_course_discount_type" verbose_name = " Course offer type " verbose_name_plural = " Course offer type " def __str__(self): return "%s" % (self.name) class CourseDiscount(BaseModel): """ Course discount model """ discount_type = models.ForeignKey("CourseDiscountType", on_delete=models.CASCADE, related_name='coursediscounts', verbose_name=" Type of offer ") condition = models.IntegerField(blank=True, default=0, verbose_name=" Meet the favorable price conditions ",help_text=" Set the price threshold for participating in the offer , Indicates that the product must be in xx Only when the price is above the price can we participate in the preferential activities ,<br> If you don't fill in , There is no threshold ") # Because some courses are not enough 100, You're reducing 100, I lost money sale = models.TextField(verbose_name=" Preferential formula ",blank=True,null=True, help_text=""" If not, it means free ;<br> * The beginning of the sign indicates the discount price , for example *0.82 20% off ;<br> - The beginning of the sign means a reduction , for example -20 It means the original price -20;<br> If you need to say full minus , You need to use The original price - Favorable Price , For example, it means that the course price is higher than 100, Discount 10; Greater than 200, Discount 25, The format is as follows :<br> full 100-10<br> full 200-25<br> """) class Meta: db_table = "ly_course_discount" verbose_name = " Preferential price strategy " verbose_name_plural = " Preferential price strategy " def __str__(self): return " Price concessions :%s, Preferential terms :%s, Preferential value :%s" % (self.discount_type.name, self.condition, self.sale) class Activity(BaseModel): """ Special offers """ name = models.CharField(max_length=150, verbose_name=" The name of the event ") start_time = models.DateTimeField(verbose_name=" The start time of the discount strategy ") end_time = models.DateTimeField(verbose_name=" The end time of the offer ") remark = models.CharField(max_length=250, blank=True, null=True, verbose_name=" Notes ") class Meta: db_table = "ly_activity" verbose_name=" Commodity activities " verbose_name_plural=" Commodity activities " def __str__(self): return self.name class CoursePriceDiscount(BaseModel): """ The relationship between courses and preferential strategies """ course = models.ForeignKey("Course",on_delete=models.CASCADE, related_name="activeprices",verbose_name=" Course ") active = models.ForeignKey("Activity",on_delete=models.DO_NOTHING, related_name="activecourses",verbose_name=" Activities ") discount = models.ForeignKey("CourseDiscount",on_delete=models.CASCADE,related_name="discountcourse",verbose_name=" Discount ") class Meta: db_table = "ly_course_price_dicount" verbose_name=" The relationship between courses and preferential strategies " verbose_name_plural=" The relationship between courses and preferential strategies " def __str__(self): return " Course :%s, Special offers : %s, Starting time :%s, End time :%s" % (self.course.name, self.active.name, self.active.start_time,self.active.end_time)
class Course: def activity(self): import datetime now = datetime.datetime.now() # Get the name of the activity that the course participated in activity_list = self.activeprices.filter(is_show=True, is_deleted=False, active__start_time__lte=now, active__end_time__gte=now) return activity_list # Offer type name def discount_name(self): dis_name = '' a = self.activity() if a: discount_n_list = [] for i in a: # Get the discount type name of the course discount_n = i.discount.discount_type.name discount_n_list.append(discount_n) dis_name = discount_n_list[0] return dis_name
2.course/serializers.py
The serializer adds the field
class CourseModelSerializer: field = [,discount_name]
class CourseDetailModelSerializer: field = [,discount_name]
3.drf test :course/courses
3. The course list page shows the real price
1.dev.py
USE_TZ = False # Change the time zone
2.course/models.py
class Course: def real_price(self): price = float(self.price) # Get the real price r_price = price a = self.activity() # Get the activity name corresponding to the course if a: # If the course takes part in an activity sale = a[0].discount.sale # Check the discount formula for the event condition_price = a[0].discount.condition # Check the price conditions that meet the discount for the event # 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) # Full reduction full 100-15 elif ' full ' in sale: if price >= condition_price: # Only if the price meets the preferential conditions can the price be fully reduced l1 = sale.split('\r\n') dis_list = [] #10 50 25 for i in l1: a, b = i[1:].split('-') # When the price of goods (400) Satisfy 100-200-300 Should choose full 300 That discount if price >= float(a): dis_list.append(float(b)) max_dis = max(dis_list) # Get the biggest discount r_price = price - max_dis # The original price - Full price reduction = The real price return r_price
3.course/serializers.py
class CourseModelSerializer: field = [,discount_name,real_price]
class CourseDetailModelSerializer: field = [,discount_name,real_price]
4.drf test :course/courses
4. Display the name of the offer type and the real price on the front page
1. The front of the course list page
<!-- Course.vue --> <div class="pay-box"> <span class="discount-type" v-if="course.discount_name">{{course.discount_name}}</span> <span class="discount-price">¥{{course.real_price}} element </span> <span class="original-price" v-if="course.discount_name"> The original price :{{course.price}} element </span> <span class="buy-now"> Buy now </span> </div>
2. At the front of the course details page
<!-- Detail.vue --> <div class="sale-time"> <p class="sale-type">{{course_data.discount_name}}</p> <p class="expire"> The end of the distance : Only left 567 God 12 Hours 52 branch <span class="second">32</span> second </p> </div> <p class="course-price"> <span> Activity price </span> <span class="discount">¥{{course_data.real_price}}</span> <span class="original">¥{{course_data.price}}</span> </p>
5. The course list page shows the specific end time
1.course/models.py
class Course: def left_time(self): import datetime now = datetime.datetime.now().timestamp() # Get the current timestamp left_t = 0 a = self.activity() # Get the activities of the current course if a: # If there are activities in the current course end_time = a[0].active.end_time.timestamp() # Get the end time of the current course activity left_t = end_time - now # The rest of the time = End time - current time return left_t
2. The serializer puts left_time
course/serializers.py
class CourseDetailModelSerializer: field = [,discount_name,real_price,left_time]
3. Front end rendering to end time
Detail.vue
<!-- html --> <div class="sale-time"> <p class="sale-type">{{course_data.discount_name}}</p> <p class="expire"> The end of the distance : Only left {{course_data.left_time/60/60/24 | pInt}} God {{course_data.left_time/60/60 % 24| pInt}} Hours {{course_data.left_time/60 % 60 | pInt}} branch <span class="second">{{course_data.left_time % 60 | pInt}}</span> second </p> </div>
stay js Part needs to set a timer , Let time be reduced all the time 1s
Detail.vue
get_course_data(){ this.$axios.get(`${this.$settings.Host}/course/detail/${this.course_id}/`) .then((res)=>{ //console.log(res.data); this.course_data = res.data; this.playerOptions.sources[0].src = res.data.course_video this.playerOptions.poster = res.data.course_img // Set timer setInterval(()=>{ this.course_data.left_time--; },1000) }) },
Add : Let the page show 0x branch 0x Second effect → filter
Detail.vue
<!-- Detail.vue--html --> <p class="expire"> The end of the distance : Only left {{course_data.left_time/60/60/24 | pInt}} God
{{course_data.left_time/60/60 % 24| pInt}} Hours
{{course_data.left_time/60 % 60 | pInt}} branch
<span class="second">{{course_data.left_time % 60 | pInt}}</span> second </p>
// Detail.vue--js filters:{ pInt(val){ let a = parseInt(val); if (a < 10){ a = `0${a}`; } return a } }
2. Add cart / Check the user authentication of the shopping cart
Add... To the view IsAuthenticated User authentication
# cart/views.py class AddCartView: # The request head must carry token permission_classes = [IsAuthenticated,] # Add user authentication def add: user_id = request.user.id # Get real users id ''' request = user_obj '''
drf Interface : cart/add_cart Data not available , Because of the addition of IsAuthenticated authentication
Add a shopping cart with token Otherwise, you can't add a shopping cart
// Detail.vue Add cart methods: { addCart(){ let token = localStorage.token || sessionStorage.token; if (token){ this.$axios.post(`${this.$settings.Host}/users/verify/`,{ token:token, }).then((res)=>{ this.$axios.post(`${this.$settings.Host}/cart/add_cart/`,{ course_id:this.course_id, },{ // To submit data to the back end, you need to add header term , take token Also submit to the past headers:{ 'Authorization':'jwt ' + token } }).then((res)=>{ this.$message.success(res.data.msg); console.log('>>>>>',this.$store) this.$store.commit('add_cart', res.data.cart_length) ; console.log(this.$store.state); }) }).catch((error)=>{ ...... }) } else { ...... }) } },
You need to carry with you when you check the shopping cart token Otherwise, you can't add a shopping cart
// Cart.vue Shopping cart page created() { let token = sessionStorage.token || localStorage.token; if (token){ this.$axios.get(`${this.$settings.Host}/cart/add_cart/`,{ // You should also carry the shopping cart page token Otherwise you can't view headers:{ 'Authorization':'jwt ' + token } }) .then((res)=>{ this.cart_data_list = res.data.cart_data_list }) .catch((error)=>{ this.$message.error(error.response.data.msg); })
# cart/views.py class AddCartView: def cart_list(self, request): ...... cart_data_list.append({ ...... 'real_price': course_obj.real_price(), ...... }) ......
<!-- cartitem.vue --> <div class="cart_column column_4">¥{{cart.real_price}}</div>
# cart/views.py class AddCartView: def change_select(self, request): # Get the course id course_id = request.data.get('course_id') # Calibration course id Legitimacy try: models.Course.objects.get(id=course_id) except: return Response({'msg': ' The curriculum doesn't exist , Don't mess with !'}, status=status.HTTP_400_BAD_REQUEST) # Get user_id user_id = request.user.id # Go to redis database :cart conn = get_redis_connection('cart') # redis Save the data - Save in sets : user id: Check the course id conn.sadd('selected_cart_%s' % user_id, course_id) return Response({'msg':' Check success '}) def cancel_select(self, request): course_id = request.data.get('course_id') try: models.Course.objects.get(id=course_id) except: return Response({'msg': ' The curriculum doesn't exist , Don't mess with !'}, status=status.HTTP_400_BAD_REQUEST) user_id = request.user.id conn = get_redis_connection('cart') # 1:{1,3} conn.srem('selected_cart_%s' % user_id, course_id) return Response({'msg': ' congratulations ! Less money , But do you really stop learning !'})
Configure routing for both functions
# cart/urls.py from django.urls import path,re_path from . import views urlpatterns = [ path('add_cart/', views.AddCartView.as_view({'post':'add',
'get':'cart_list',
'patch':'change_select',
'put':'cancel_select'})) # Different request methods take different functions ]
<!-- When the user clicks on the check box in front of it , Will change selected Value Close and will be watch Listen to the In monitoring Whether it is checked or not, the parent label will be triggered to recalculate the price (this.$emit) --> <el-checkbox class="my_el_checkbox" v-model="cart.selected"></el-checkbox>
// Cartitem.vue watch:{ 'cart.selected':function (){ // Add the check let token = localStorage.token || sessionStorage.token; if (this.cart.selected){ this.$axios.patch(`${this.$settings.Host}/cart/add_cart/`,{ course_id: this.cart.course_id, },{ headers:{ 'Authorization':'jwt ' + token } }).then((res)=>{ this.$message.success(res.data.msg); this.$emit('cal_t_p') // Trigger cart How to calculate the total commodity price }).catch((error)=>{ this.$message.error(res.data.msg); }) } else { // Deselect this.$axios.put(`${this.$settings.Host}/cart/add_cart/`,{ course_id: this.cart.course_id, },{ headers:{ 'Authorization':'jwt ' + token } }).then((res)=>{ this.$message.success(res.data.msg); this.$emit('cal_t_p') // Trigger cart How to calculate the total commodity price }).catch((error)=>{ this.$message.error(res.data.msg); }) } }, }
Trigger Cart Components ( Parent component ) How to calculate the total price of goods
<!-- 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"></CartItem> </div>
// Cart.vue methods:{ cal_total_price(){ let t_price = 0 this.cart_data_list.forEach((v,k)=>{ // v Is the value k It's the index if (v.selected){ t_price += v.real_price } }) this.total_price = t_price } }
4. Shopping cart list shows - Back end interface
# cart/views.py 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') # Take out the course from the user's shopping cart id(redis There is ) expire_id = eid.decode('utf-8') # Take out the expiration date from the user's shopping cart id(redis There is ) course_obj = models.Course.objects.get(id=course_id) # According to the course id, adopt ORM Query to get the course object , In the following, you can use the course object . Field Get the parameter information of the corresponding course cart_data_list.append({ 'course_id': course_obj.id, 'name': course_obj.name, 'course_img': constants.SERVER_ADDR + course_obj.course_img.url, 'price': course_obj.price, 'real_price': course_obj.real_price(), 'expire_id': expire_id, '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})
BUG: Check two courses Refresh the page redis There are still two courses in id
# cart/views.py def cart_list(self, request): ...... conn = get_redis_connection('cart') # When the user refreshes the page , from redis Delete the course corresponding to the user in id conn.delete('selected_cart_%s' % user_id) ret = conn.hgetall('cart_%s' % user_id) ......