你好,我尝试使用 Django 构建一个电子商务应用程序,我有一个购物车类,可以与会话一起使用,这是我的购物车类:
from django.conf import settings
from shop.models import Product, ProductColor
from django.shortcuts import get_object_or_404
from django.utils import timezone
class Cart:
def __init__(self, request_or_session_data):
if hasattr(request_or_session_data, 'session'): # Check if it's a request
self.session = request_or_session_data.session
else:
# If it's session data, use it directly
self.session = request_or_session_data
cart = self.session.get(settings.CART_SESSION_ID)
if not cart:
cart = self.session[settings.CART_SESSION_ID] = {}
self.cart = cart
self.total_discount = None
self.discount_code = None
self.have_discount_code = self.session.get("have_discount_code", False)
self.discount_code_amount = self.session.get("discount_code_amount", 0)
self.last_updated = self.session.get("last_updated")
if hasattr(request_or_session_data, 'user') and request_or_session_data.user.is_authenticated:
self.user = request_or_session_data.user
def add(self, product, color_id, quantity=1, override_quantity=False):
product_id = str(product.id)
color = get_object_or_404(ProductColor, id=color_id)
cart_key = f"{product_id}_{color_id}"
if cart_key not in self.cart:
self.cart[cart_key] = {
'color_id': str(color.id),
'quantity': 0,
'price': str(product.price),
'discount': str(product.discount),
'discount_price': str(product.get_discounted_price()),
}
if override_quantity:
self.cart[cart_key]['quantity'] = quantity
else:
self.cart[cart_key]['quantity'] += quantity
self.total_discount = self.get_total_discount()
self.save()
def mark_session_modified(self):
# Mark session as modified if `self.session` is a Django session object
if hasattr(self.session, 'modified'):
self.session.modified = True
def save(self):
# mark the session as "modified" to make sure it gets saved
self.last_updated = timezone.now()
self.session[settings.CART_SESSION_ID] = self.cart
self.session['have_discount_code'] = self.have_discount_code
self.session['discount_code_amount'] = self.discount_code_amount
self.session['last_updated'] = self.last_updated.isoformat()
if hasattr(self, 'user') and self.user:
self.session["user_id"] = self.user.id
self.mark_session_modified()
def remove(self, product, color_id=None):
product_id = str(product.id)
if color_id:
cart_key = f"{product_id}_{color_id}"
if cart_key in self.cart:
del self.cart[cart_key]
else:
# If no color_id is provided, remove all items related to the product_id
keys_to_remove = [key for key in self.cart if key.startswith(product_id)]
for key in keys_to_remove:
del self.cart[key]
if len(self.cart) == 0:
self.clear() # Call the clear method to remove discount codes and other data
else:
self.total_discount = self.get_total_discount() # Update total discount
self.save()
def __iter__(self):
cart = self.cart.copy()
product_ids = {key.split('_')[0] for key in cart.keys()} # Extract product IDs
products = Product.objects.filter(id__in=product_ids)
for product in products:
for key in cart.keys():
if key.startswith(str(product.id)):
item = cart[key]
item["product"] = product
# Extract color ID from the key
color_id = key.split('_')[1] if '_' in key else None
# Get the corresponding ProductColor instance
if color_id:
try:
product_color = ProductColor.objects.get(id=color_id, product=product)
item["color_name"] = product_color.color # Use the color from ColorChoices
except ProductColor.DoesNotExist:
item["color_name"] = "Unknown Color"
else:
item["color_name"] = "No Color"
item["color_id"] = str(color_id)
item["product_id"] = str(product.id)
item["price"] = int(item["price"])
item["discount_price"] = int(item["discount_price"])
if item["discount"] == "0":
item["total_price"] = item["price"] * item["quantity"]
else:
item["total_price"] = item["discount_price"] * item["quantity"]
yield item
def __len__(self):
return sum(item['quantity'] for item in self.cart.values())
def get_total_price(self):
total_price = 0
for item in self:
total_price += item["total_price"]
if self.have_discount_code:
discount_factor = self.discount_code_amount / 100
total_price = int(total_price * (1 - discount_factor))
return total_price
def add_coupon(self, amount, code):
self.discount_code_amount = amount
self.have_discount_code = True
self.discount_code = code
self.save()
def get_total_discount(self):
total_discount = 0
for item in self.cart.values():
if item["discount"] != "0":
discount = int(item["price"]) - int(item["discount_price"])
total_discount += discount * item["quantity"]
return total_discount
@property
def total_get_price_of_discount(self):
total_price = 0
for item in self:
total_price += item["total_price"]
price = self.get_total_price()
return total_price - price
def clear(self):
# remove cart from session
del self.session[settings.CART_SESSION_ID]
self.have_discount_code = False
self.discount_code = None
self.discount_code_amount = 0
self.total_discount = None
self.save()
每次用户将产品添加到购物车时,我都会减少产品数量,并且我有一个任务是检查他们的购物车是否闲置超过 30 分钟,首先向用户发送短信并提醒他们添加购物车,然后再进行操作1小时清除购物车并将数量添加回产品问题是在任务中我尝试循环到所有会话并使用 session_data["sms_sent"] = True 修改该会话之后当超过 1 小时时,也会再次发送短信,它将数量添加回数据库,但无法从会话中清除购物车
这是我正在使用的任务:
@shared_task
def clear_abandoned_carts():
expired_time = timezone.now() - CART_EXPIRATION_TIME
warning_time = timezone.now() - SEND_WARNING_TIME
for session in Session.objects.filter(expire_date__gte=timezone.now()):
data = session.get_decoded()
last_updated_str = data.get("last_updated")
cart = data.get(settings.CART_SESSION_ID)
print("sms_sent flag before:", data.get("sms_sent")) # Safe access
if cart and last_updated_str:
last_updated = timezone.datetime.fromisoformat(last_updated_str)
if last_updated < expired_time:
# Clear cart if expired
for key, item in cart.items():
color_id = item["color_id"]
quantity = item["quantity"]
try:
remove_from_cart(color_id, int(quantity))
except ProductColor.DoesNotExist:
pass # Handle as needed
# Clear session data
session_data = session.get_decoded()
session_data.pop(settings.CART_SESSION_ID, None)
session_data.pop("last_updated", None)
session_data.pop("have_discount_code", None)
session_data.pop("discount_code_amount", None)
session_data.pop("sms_sent", None)
session_data["modified"] = True
session.save()
cart_instance = Cart(data)
cart_instance.clear()
elif last_updated < warning_time:
# Check if the SMS warning was already sent
if not data.get("sms_sent"): # Proceed only if SMS hasn't been sent
user_id = data.get("user_id")
if user_id:
try:
user = CustomUser.objects.get(id=user_id)
if user.phone_number:
phone_number = user.phone_number
send_cart_warning(int(phone_number)) # Send SMS
# Mark SMS as sent in session data
session_data = session.get_decoded()
session_data["sms_sent"] = True # Safely update session
session_data["modified"] = True
session.modified = True
session.save()
print("sms_sent flag after:", session_data.get("sms_sent")) # Safe access
except CustomUser.DoesNotExist:
pass
在控制台中,当其发送消息时,此 print("sms_sent flag after:", session_data.get("sms_sent")) 给我 True 但当它再次运行时,此 print("sms_sent flag before:", data.get( “sms_sent”))给我没有你们能帮我解决这个问题吗
在 Celery 任务中更新会话数据时,请确保会话被标记为已修改并正确保存。例如:
@shared_task
def clear_abandoned_carts():
expired_time = timezone.now() - CART_EXPIRATION_TIME
warning_time = timezone.now() - SEND_WARNING_TIME
for session in Session.objects.filter(expire_date__gte=timezone.now()):
data = session.get_decoded()
last_updated_str = data.get("last_updated")
cart = data.get(settings.CART_SESSION_ID)
print("sms_sent flag before:", data.get("sms_sent")) # Safe access
if cart and last_updated_str:
last_updated = timezone.datetime.fromisoformat(last_updated_str)
if last_updated < expired_time:
# Clear cart if expired
for key, item in cart.items():
color_id = item["color_id"]
quantity = item["quantity"]
try:
remove_from_cart(color_id, int(quantity))
except ProductColor.DoesNotExist:
pass # Handle as needed
# Clear session data
session_data = session.get_decoded()
session_data.pop(settings.CART_SESSION_ID, None)
session_data.pop("last_updated", None)
session_data.pop("have_discount_code", None)
session_data.pop("discount_code_amount", None)
session_data.pop("sms_sent", None)
# Mark session as modified and save
session_data["modified"] = True
session.session_data = Session.objects.encode(session_data)
session.save()
elif last_updated < warning_time:
# Check if the SMS warning was already sent
if not data.get("sms_sent"):
user_id = data.get("user_id")
if user_id:
try:
user = CustomUser.objects.get(id=user_id)
if user.phone_number:
phone_number = user.phone_number
send_cart_warning(int(phone_number)) # Send SMS
# Mark SMS as sent in session data
session_data = session.get_decoded()
session_data["sms_sent"] = True
session_data["modified"] = True
session.session_data = Session.objects.encode(session_data)
session.save()
except CustomUser.DoesNotExist:
pass
确保从会话中删除购物车数据,然后保存会话。
def clear(self):
# Remove cart-related data from session
keys_to_remove = [
settings.CART_SESSION_ID,
'have_discount_code',
'discount_code_amount',
'last_updated',
'sms_sent'
]
for key in keys_to_remove:
self.session.pop(key, None)
# Mark session as modified and save
self.mark_session_modified()