Django使用session管理购物车

购物车允许用户选择产品并设置他们想要订购的数量,然后在他们浏览网站时临时存储这些信息,直到最终下订单。购物车必须在会话中持久化,以便在用户访问期间维护购物车项目。

使用Django的session框架来持久化购物车。购物车将保持在session中,直到它完成或用户签出。

创建一个可以序列化为JSON的简单结构,以便在会话中存储购物车项目。购物车必须为其中包含的每个项目包含以下数据:

  •  Product实例的ID
  •  为产品选择的数量
  •  产品的单价

由于产品价格可能会有所不同,因此将产品添加到购物车时将其价格与产品本身一起存储。通过这样做,当用户将产品添加到购物车时,我们使用产品的当前价格,而不管产品的价格随后是否发生了变化。

创建一个管理购物车的应用程序。打开终端并创建一个新的应用程序,在项目目录下运行以下命令:

python manage.py startapp cart

编辑项目的settings.py文件,并将新应用程序添加到INSTALLED_APPS设置如下:

INSTALLED_APPS = [# ...'shop.apps.ShopConfig','cart.apps.CartConfig',
]

关于shop应用,请查看Django初创shop应用-CSDN博客

构建功能来创建购物车并将它们与会话关联起来。购物车的工作原理如下:
当需要购物车时,我们检查是否设置了自定义会话密钥。如果会话中没有设置购物车,将创建一个新购物车并将其保存在购物车会话密钥中。

定制类


在cart应用程序目录中创建一个cart.py文件,并向其中添加以下代码:

from decimal import Decimal
from shop.models import ProductCART_SESSION_ID = 'cart'class Cart(object):def __init__(self,request):self.session = request.sessioncart = self.session.get(CART_SESSION_ID)if not cart:cart = self.session[CART_SESSION_ID] = {}self.cart = cartdef add(self, product, quantity=1, update_quantity=False):product_id = str(product.id)if product_id not in self.cart:self.cart[product_id] = {'quantity':0,'price':str(product.price)}if update_quantity:self.cart[product_id]['quantity'] = quantityelse:self.cart[product_id]['quantity'] += quantityself.save()def save(self):self.session.modified = Truedef remove(self, product):product_id = str(product.id)print(self.cart)if product_id in self.cart:del self.cart[product_id]self.save()def __iter__(self):product_ids = self.cart.keys()products = Product.objects.filter(id__in=product_ids)cart = self.cart.copy()for product in products:cart[str(product.id)]['product'] = productfor item in cart.values():item['price'] = Decimal(item['price'])item['total_price'] = item['price']* item['quantity']yield itemdef __len__(self):return sum(item['quantity'] for item in self.cart.values())def get_total_price(self):return sum(Decimal(item['price']) * item['quantity'] for item in self.cart.values())

CART_SESSION_ID = 'cart' 这是将用于在用户会话中存储购物车的键。因为Django会话是按访问者管理的,所以我们可以对所有会话使用相同的cart会话键。

这是管理购物车的Cart类。要求用请求对象初始化购物车。使用self存储当前会话。Session =request.session,使其可被Cart类的其他方法访问。


首先,使用self.session.get(settings.CART_SESSION_ID)从当前会话获取购物车。如果会话中没有购物车,我们通过在会话中设置一个空字典来创建一个空购物车。我们希望购物车字典使用产品id作为键,并使用数量和价格作为值的字典

  • add()方法接受以下参数作为输入:
  • product:要在购物车中添加或更新的产品实例。
  • quantity:带产品数量的可选整数。默认为1。
  • update_quantity:这是一个布尔值,指示是否需要用给定的数量更新数量(True),或者是否必须将新数量添加到现有数量(False)。

使用产品ID作为购物车内容字典中的键。将产品ID转换为字符串,因为Django使用JSON来序列化会话数据,而JSON只允许字符串键名。
产品ID是键,持久化的值是一个包含产品数量和价格数字的字典。产品的价格从十进制转换为字符串,以便对其进行序列化。最后,我们调用save()方法在会话中保存购物车。
save()方法将会话标记为使用session修改的会话。modified = True。

remove()方法从购物车字典中删除给定的产品,并调用save()方法在会话中更新购物车。

我们必须遍历购物车中包含的项目并访问相关的产品实例。为此,在类中定义__iter__()方法。
在__iter__()方法中,检索购物车中存在的Product实例,以便将它们包含在购物车项中。我们将当前购物车复制到购物车变量中,并添加产品实例到它。最后,遍历购物车项目,将项目的价格转换回十进制,并为每个项目添加total_price属性。现在,可以很容易地遍历购物车中的项目。

创建视图

为了向购物车中添加商品,需要一个允许用户选择数量的表单。
在cart应用程序目录中创建一个forms.py文件,并向其中添加以下代码:

from django import formsPRODUCT_QUANTITY_CHOICES = [(i, str(i)) for i in range(1, 21)]class CartAddProductForm(forms.Form):quantity = forms.TypedChoiceField(choices=PRODUCT_QUANTITY_CHOICES,coerce=int)update = forms.BooleanField(required=False,initial=False,widget=forms.HiddenInput)

PRODUCT_QUANTITY_CHOICES会得到一个列表:
[(1, '1'), (2, '2'), (3, '3'), (4, '4'), (5, '5'), (6, '6'), (7, '7'), (8, '8'), (9, '9'), (10, '10'), (11, '11'), (12, '12'), (13, '13'), (14, '14'), (15, '15'), (16, '16'), (17, '17'), (18, '18'), (19, '19'), (20, '20')]

  •  quantity:允许用户在1-20之间选择数量。用TypedChoiceField字段,带coerce=int将输入转换为整数。
  •  update:指示是否必须将数量添加到此产品的购物车中的任何现有数量(False),或者是否必须使用给定数量更新现有数量(True)。用HiddenInput小部件,因为我们不想将其显示给用户。

编辑cart应用程序的views.py文件,并向其中添加以下代码:

from django.shortcuts import render,redirect,get_object_or_404
from django.views.decorators.http import require_POST
from shop.models import Product
from .cart import Cart
from .forms import CartAddProductForm@require_POST
def cart_add(request,product_id):cart = Cart(request)product = get_object_or_404(Product,id=product_id)form = CartAddProductForm(request.POST)if form.is_valid():cd = form.cleaned_datacart.add(product=product,quantity=cd['quantity'],update_quantity=cd['update'])return redirect('cart:cart_detail')def cart_remove(request,product_id):cart = Cart(request)product = get_object_or_404(Product,id=product_id)cart.remove(product)return redirect('cart:cart_detail')def cart_detail(request):cart = Cart(request)return render(request,'cart/detail.html',{'cart':cart})

这是用于向购物车添加产品或更新现有产品数量的视图。
使用require_POST装饰器只允许POST请求,因为这个视图将改变数据。
视图接收产品ID作为参数。检索具有给定ID的Product实例并验证CartAddProductForm。如果表单有效,添加或更新购物车中的产品。视图将重定向到cart_detail URL,该URL将显示购物车的内容。

cart_remove视图接收产品ID作为参数。检索具有给定ID的Product实例,并从购物车中删除产品。然后,我们将用户重定向到cart_detail URL。

cart_detail视图获取当前购物车以显示它。

创建URL


现在为这些视图添加URL模式。在cart应用程序目录中创建一个新文件,并将其命名为urls.py。添加以下url到它:

from django.urls import path
from . import viewsapp_name = 'cart'urlpatterns = [path('',views.cart_detail, name='cart_detail'),path('add/<int:product_id>/',views.cart_add,name='cart_add'),path('remove/<int:product_id>/',views.cart_remove,name='cart_remove'),
]

编辑mysite项目的main URLs .py文件,添加以下URL模式来包含购物车URL:

urlpatterns = [#...path("shop/",include('shop.urls',namespace='shop')),path("cart/",include('cart.urls',namespace='cart')),]

创建模版

cart_add和cart_remove视图不呈现任何模板,但是需要为cart_detail视图创建一个模板来显示购物车项目和总数。
创建cart/templates/cart/detail.html

{% extends "shop/base.html" %}
{% load static %}
{% block title %}Your shopping cart
{% endblock %}
{% block content %}<h1>Your shopping cart</h1><table class="table"><thead><tr class="table-primary"><th>Image</th><th>Product</th><th>Quantity</th><th>Remove</th><th>Unit price</th><th>Price</th></tr></thead><tbody>{% for item in cart %}{% with product=item.product %}<tr><td><a href="{{ product.get_absolute_url }}"><img width="100px" height="50px" src="{% if product.image %}{{ product.image.url }}{% else %}{% static 'img/no_image.png' %}{% endif %}"></a></td><td>{{ product.name }}</td><td>{{ item.quantity }}</td><td><a href="{% url 'cart:cart_remove' product.id %}">Remove</a></td><td class="num">${{ item.price }}</td><td class="num">${{ item.total_price }}</td></tr>{% endwith %}{% endfor %}<tr class="table-primary"><td>Total</td><td colspan="4"></td><td class="num">${{ cart.get_total_price }}</td></tr></tbody></table><p style="text-align: right;"><a href="{% url 'shop:product_list' %}" class="buttonlight">Continue shopping</a><a href="#" class="button">Checkout</a></p>
{% endblock %}

这是用于显示购物车内容的模板。它包含一个表,其中包含存储在当前购物车中的项目。
用户使用发布到cart_add视图的表单来更改所选产品的数量。我们还允许用户通过为每个项目提供remove链接来从购物车中删除项目。

集成到shop应用


现在需要将“添加到购物车”按钮添加到产品详细信息页面。编辑shop应用程序的views.py文件,将CartAddProductForm添加到product_detail视图中,如下所示:

def product_detail(request,id,slug):product = get_object_or_404(Product,id=id,slug=slug,available=True)cart_product_form = CartAddProductForm()template = 'shop/product/detail.html'context = {'product':product,'cart_product_form':cart_product_form}return render(request,template,context)

编辑shop应用程序的shop/product/detail.html模板,并将以下表单添加到产品的价格中,如下所示:

<form action="{% url 'cart:cart_add' product.id %}" method="post">
<div class="d-flex justify-content-start" style="margin-top: 50px;"><div class="p-2">{{ cart_product_form }}{% csrf_token %}</div><div class="p-2"><input class="btn btn-primary" type="submit" value="Add to cart"></div>
</div>
</form>

当用户看到购物车时,他们可能希望在下订单之前更改产品数量。我们将允许用户从购物车详细信息页面更改数量。
编辑cart应用程序的views.py文件,并将cart_detail视图更改为:

def cart_detail(request):cart = Cart(request)for item in cart:item['update_quantity_form'] = CartAddProductForm(initial={'quantity':item['quantity'],'update':True})return render(request,'cart/detail.html',{'cart':cart})

为购物车中的每个商品创建一个CartAddProductForm实例,以允许更改产品数量。
用当前商品数量初始化表单,并将update字段设置为True,这样当我们将表单提交给cart_add视图时,当前数量将被新的数量所替换。

编辑cart应用程序的cart/detail.html模板,找到以下行:

<td>{{ item.quantity }}</td>

替换为

<td>
<form action="{% url 'cart:cart_add' product.id %}" method="POST">{{ item.update_quantity_form.quantity }}{{ item.update_quantity_form.update }}<input type="submit" value="Update" class="btn btn-primary">{% csrf_token %}
</form>
</td>

重启服务,可以在购物车页面修改数量了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/635342.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微软使其AI驱动的阅读导师免费

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Flutter GetX 之 国际化

今天给大家介绍一下 GetX 的国际化功能,在日常开发过程中,我们经常会使用到国际化功能,需要们的应用支持 国际化,例如我们需要支持 简体、繁体、英文等等。 上几篇文章介绍了GetX的 路由管理 和 状态管理,看到大家的点赞和收藏,还是很开心的,说明这两篇文章给大家起到了…

基于Django的Python应用—学习笔记—功能完善

一、让用户可以输入信息 创建forms.py 创建基于表单的页面的方法几乎与前面创建网页一样&#xff1a;定义一个 URL &#xff0c;编写一个视图函数并编写一个模板。一个主要差别是&#xff0c;需要导入包含表单 的模块forms.py 。 from django import forms from .models impor…

Facebook的区块链之路:探秘数字货币的未来

近年来&#xff0c;Facebook一直在积极探索区块链技术&#xff0c;并逐渐将目光聚焦在数字货币领域。从推出Libra项目到改名为Diem&#xff0c;Facebook一直在寻求在数字货币领域取得突破性进展。本文将深入探讨Facebook的区块链之路&#xff0c;揭示其对数字货币未来发展的影响…

Red Hat Enterprise Linux 8.9 安装图解

引导和开始安装 选择倒计时结束前&#xff0c;通过键盘上下键选择下图框选项&#xff0c;启动图形化安装过程。需要注意的不同主板默认或者自行配置的固件类型不一致&#xff0c;引导界面有所不同。也就是说使用UEFI和BIOS的安装引导界面是不同的&#xff0c;如图所示。若手动调…

链表存数相加算法(leetcode第2题)

题目描述&#xff1a; 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。你可以假设除了数字 0 之外&#xff0c;这…

ZVB4/ZVB8罗德与施瓦茨ZVB8网络分析仪8GHz

德国罗德与施瓦茨ZVB8网络分析仪8GHz R&S ZVB8 网络分析仪通过两个或四个测试端口提供高达 8 GHz 的高测量速度。ZVB8 结合了出色的性能、轻巧的设计和紧凑的设计。智能和用户友好的功能提供了最大的操作便利性。R&S ZVB8 网络分析仪可以轻松处理涉及多端口和平衡测量的…

2.C语言——控制语句

控制语句 1.分支语句/判断语句if 语句if...else 语句if...else if...else语句 switch语句 2.循环语句 while 语句 do...while 语句 for 语句 3.转向语句 break continue go to 1.分支语句/判断语句 if 语句 if(boolean_expression) { /* 如果布尔表达式为真将执行的语句 */ } …

UI开发布局-HarmonyOS应用UI开发布局

UI页面的构建不用再像Android开发过程中在.xml文件中书写&#xff0c;可直接在页面上使用声明式UI的方式按照布局进行排列&#xff0c;构建应用的页面。 如下代码使用Row、Column构建一个页面布局&#xff0c;在页面布局中添加组件Text、Button&#xff0c;共同构成页面&#…

C#使用DateTime.Now静态属性动态获得系统当前日期和时间

目录 一、实例 1.源码 2.生成效果 二、相关知识点 1.Thread类 &#xff08;1&#xff09;Thread.Sleep()方法 &#xff08;2&#xff09;Thread(ThreadStart) &#xff08;3&#xff09;IsBackground &#xff08;4&#xff09;Invoke( &#xff09; 2.CreateGrap…

前端打同一个包可以从测试晋升到生产的配置方案

前端打同一个包从测试晋升到生产环境的方案&#xff0c;是一种高效、可靠且易于维护的部署方式。在这种方案中&#xff0c;前端代码在开发完成后&#xff0c;经过测试验证无误后&#xff0c;可以直接打包部署到生产环境&#xff0c;无需进行额外的配置或修改。这样可以减少部署…

面试题:40亿个QQ号,限制1G内存,如何去重?

文章目录 概要什么是BitMap&#xff1f;有什么用&#xff1f;什么是布隆过滤器&#xff0c;实现原理是什么&#xff1f;应用场景如何使用 概要 40亿个unsigned int&#xff0c;如果直接用内存存储的话&#xff0c;需要&#xff1a; 4*4000000000 /1024/1024/1024 14.9G &…

关于datagrip的一个错误。Unexpected update count received (Actual: 3, Expected: 1).

这一行原本的值是<null><null><null>,现在我们把它修改为1,114&#xff0c;无名氏&#xff0c;但却报错。 这是对应的sql语句&#xff0c;原因在于有三行全为 <null><null><null>&#xff0c;where无法指定是哪一行&#xff0c;所以看起来…

工程师职称评审的流程

职称评审是对专业技术人员的专业考核评级&#xff0c;通过公平、工作的评审工作选拔优秀且专业的人才。职称评审的流程通常包括以下几个步骤&#xff1a; 公告评审标准和要求&#xff1a;评审机构根据不同行业、专业和职业领域的要求&#xff0c;制定相应的评审标准和要求&…

Visual Studio中,每次新建文件都会自动出现提前设置好的头文件配置方法

主要是修改 newcfile.cpp 文件&#xff0c;可以用everything或者Listary等软件直接搜索文件&#xff0c;直接跳到第4步 1.图标右击——>打开文件所在位置 2.到达IDE地址后在当前目录下找VC文件夹 3.再找 VCProjectItems 文件夹——newcfile.cpp文件 4.用记事本打开&#xff…

市场复盘总结 20240119

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 昨日主题投资 连板进级率 11/39 28.2% 二进三&#xff1a; 进级率低 43% 最常用的二种方法&#xff1a; 方…

AWS 专题学习 P5 (Classic SA、S3)

文章目录 Classic Solutions Architecture无状态 Web 应用程序&#xff1a;WhatIsTheTime.com背景 & 目标架构演进Well-Architected 5 pillars 有状态的 Web 应用程序&#xff1a;MyClothes.com背景 & 目标架构演进总结 有状态的 Web 应用程序&#xff1a;MyWordPress.…

安捷伦E8361C 网络分析仪67GHz

安捷伦E8361C 网络分析仪 E8361C 是 Agilent 的 67 GHz 网络分析仪。网络分析仪是一种功能强大的仪器&#xff0c;可以以无与伦比的精度测量射频设备的线性特性。许多行业使用网络分析仪来测试设备、测量材料和监控信号的完整性。附加功能&#xff1a; 10 MHz 至 67 GHz 94 dB…

强缓存、协商缓存(浏览器的缓存机制)是么子?

文章目录 一.为什么要用强缓存和协商缓存&#xff1f;二.什么是强缓存&#xff1f;三.什么是协商缓存&#xff1f;四.总结 一.为什么要用强缓存和协商缓存&#xff1f; 为了减少资源请求次数&#xff0c;加快资源访问速度&#xff0c;浏览器会对资源文件如图片、css文件、js文…

vue3-侦听器

侦听器 计算属性允许我们声明性地计算衍生值。 需求在状态变化时进行一些操作&#xff0c;比如更改 Dom,根据异步操作结果去修改另外的数据状态。 watch 监听异步请求结果 <script lang"ts" setup> import { ref, watch } from "vue"const ques…