KeyError в /cart/ 'product_obj' Django
В моей корзине я могу добавить один и тот же товар с разными размерами и цветами. Но случайно, когда я хочу добавить один и тот же товар с разными размерами и цветом, у меня возникает ошибка в функции корзины iter. Я проследил процесс в отладчике и обнаружил, что он каким-то образом застрял в методе модели продукта str и в итоге эта ошибка raise.
модели:
from django.db import models
from django.urls import reverse
from django.conf import settings
from colorfield.fields import ColorField
class Product(models.Model):
GENDER_MALE = 'm'
GENDER_FEMALE = 'f'
GENDER_BOTH = 'b'
GENDER_CHOICE = [
(GENDER_MALE, 'Male'),
(GENDER_FEMALE, 'Female'),
(GENDER_BOTH, 'Both')
]
name = models.CharField(max_length=200)
category = models.CharField(max_length=200)
gender = models.CharField(choices=GENDER_CHOICE, max_length=1)
sizes = models.ManyToManyField(to="store.Size", related_name="sizes")
slug = models.SlugField(unique=True, allow_unicode=True, db_collation='utf8_persian_ci')
price = models.PositiveIntegerField()
description = models.TextField()
inventory = models.IntegerField()
datetime_created = models.DateTimeField(auto_now_add=True)
datetime_modified = models.DateTimeField(auto_now=True)
discounts = models.IntegerField(default=0)
available_colors = models.ManyToManyField(to="store.Color", related_name="colors")
status = models.BooleanField(default=True)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse("product_detail", kwargs={"slug": self.slug})
class Size(models.Model):
size = models.CharField(max_length=2)
def __str__(self):
return self.size
class Color(models.Model):
color = ColorField()
name = models.CharField(max_length=200)
def __str__(self):
return self.name
class Picture(models.Model):
picture = models.ImageField(upload_to=f'static/store/images/')
product = models.ForeignKey(Product, default=None, related_name='images', on_delete=models.PROTECT)
def __str__(self):
return self.picture.url
Просмотров:
def cart_detail_view(request):
cart = Cart(request)
colors = Color.objects.all()
for item in cart:
item['product_update_quantity_form'] = AddProductToCartForm(initial={
'inplace': True,
})
return render(request, 'products/cart_detail.html', {'cart': cart, 'colors': colors})
def add_to_cart_view(request, pk):
cart = Cart(request)
product = get_object_or_404(Product, pk=pk)
form = AddProductToCartForm(request.POST)
print(request.POST)
if form.is_valid():
cleaned_data = form.cleaned_data
size = cleaned_data['size']
color = cleaned_data['color']
quantity = (cleaned_data['quantity'])
replace_current_quantity = cleaned_data['inplace']
cart.add(product, size, color, quantity, replace_current_quantity)
return redirect('cart_detail')
def remove_item_from_cart(request, cart_item):
cart = Cart(request)
cart.remove(cart_item)
return redirect('cart_detail')
@require_POST
def clear_cart(request):
cart = Cart(request)
if len(cart) != 0:
cart.clear()
return redirect('product_list')
cart.py:
from .models import Product
class Cart:
def __init__(self, request):
"""
Initialize the cart
"""
self.request = request
self.session = request.session
cart = self.session.get('cart')
if not cart:
cart = self.session['cart'] = {}
self.cart = cart
def add(self, product, size, color, quantity=1, replace_current_quantity=False):
"""
Add a product to the cart
"""
cart_item = '1'
flag = True
for item in self.cart.items():
if item[1]['product_id'] == product.id and item[1]['size'] == size and item[1]['color'] == color:
if replace_current_quantity:
item[1]['quantity'] = quantity
else:
item[1]['quantity'] += quantity
flag = False
keys = list(self.cart.keys())
if flag:
while flag:
if cart_item in keys:
cart_item = str(int(cart_item) + 1)
continue
flag = False
self.cart[cart_item] = {'product_id': product.id}
self.cart[cart_item]['size'] = size
self.cart[cart_item]['color'] = color
self.cart[cart_item]['quantity'] = quantity
self.save()
def remove(self, cart_item):
cart_item = str(cart_item)
"""
Remove a product from the cart
"""
if cart_item in self.cart.keys():
del self.cart[cart_item]
self.save()
def save(self):
"""
Mark session as modified to save changes
"""
self.session.modified = True
def __iter__(self):
product_ids = [value['product_id'] for value in self.cart.values()]
products = Product.objects.filter(id__in=product_ids)
cart = self.cart.copy()
keys = list(cart.keys())
for index,product in enumerate(products):
cart[str(keys[index])]['product_obj'] = product
cart[str(keys[index])]['cart_item'] = keys[index]
for item in cart.values():
item['total_price'] = item['product_obj'].price * item['quantity']
yield item
def __len__(self):
return len(self.cart)
def clear(self):
del self.session['cart']
self.save()
def get_total_price(self):
return sum(item['product_obj'].price * item['quantity'] for item in self.cart.values())
Урлы:
from store import views
urlpatterns = [
path('products/', views.ProductListView.as_view(), name='product_list'),
path("products/<slug>/", views.ProductDetailView.as_view(), name='product_detail'),
path("products/<slug>/comment", views.CommentView.as_view(), name='comment_create'),
path('accounts/profile/', views.profile_view, name='account_profile'),
path('cart/', views.cart_detail_view, name='cart_detail'),
path('cart/add/<int:pk>/', views.add_to_cart_view, name="cart_add"),
path('cart/remove/<int:cart_item>/', views.remove_item_from_cart, name='cart_remove'),
path('clear/', views.clear_cart, name='cart_clear'),
]
Формы:
class AddProductToCartForm(forms.Form):
QUANTITY_CHOICES = [(i, str(i)) for i in range(1, 31)]
quantity = forms.TypedChoiceField(choices=QUANTITY_CHOICES, coerce=int,)
color = forms.CharField(max_length=20)
size = forms.CharField(max_length=20)
inplace = forms.BooleanField(required=False, widget=forms.HiddenInput)
Traceback:
Environment:
Request Method: GET
Request URL: http://127.0.0.1:8000/cart/
Django Version: 5.0.1
Python Version: 3.12.1
Installed Applications:
['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'allauth',
'allauth.account',
'allauth.socialaccount',
'allauth.socialaccount.providers.google',
'widget_tweaks',
'colorfield',
'store',
'core']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'allauth.account.middleware.AccountMiddleware']
Traceback (most recent call last):
File "D:\Django\shoe_store\venv\Lib\site-packages\django\core\handlers\exception.py", line 55, in inner
response = get_response(request)
^^^^^^^^^^^^^^^^^^^^^
File "D:\Django\shoe_store\venv\Lib\site-packages\django\core\handlers\base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "D:\Django\shoe_store\store\views.py", line 67, in cart_detail_view
for item in cart:
^^^^^^
File "D:\Django\shoe_store\store\cart.py", line 81, in __iter__
item['total_price'] = item['product_obj'].price * item['quantity']
^^^^^^^^^^^^^^^^^^^
Exception Type: KeyError at /cart/
Exception Value: 'product_obj'
Я отследил процесс в отладчике и обнаружил, что он каким-то образом застрял в методе str моей модели продукта и в итоге возникла ошибка raise.
Слишком много кода. Я стараюсь включать только нужные разделы.
По сути, на первом for loop
вы заполняете объекты cart
словаря элементами с product
в качестве значения и 'product_obj'
в качестве ключа.
for index,product in enumerate(products):
cart[str(keys[index])]['product_obj'] = product
cart[str(keys[index])]['cart_item'] = keys[index]
for item in cart.values():
item['total_price'] = item['product_obj'].price * item['quantity']
yield item
Во втором for loop
вы пытаетесь получить product
(и его цену) с помощью клавиши 'product_obj'
, но python выдает ошибку KeyError
:
File "D:\Django\shoe_store\store\cart.py", line 81, in __iter__
item['total_price'] = item['product_obj'].price * item['quantity']
^^^^^^^^^^^^^^^^^^^
Exception Type: KeyError at /cart/
Exception Value: 'product_obj'
Другими словами, либо все, либо некоторые объекты товаров не имеют пар ключ-значение product_obj
и product
.
Возможно, это происходит потому, что в вашем cart
больше товаров, чем products
, поэтому в 1-м цикле объекты товаров добавляются только к нескольким элементам корзины.
Решение:
Убедитесь, что товары и корзина имеют одинаковую длину, если они должны иметь одинаковую длину, то вы должны выдать ошибку или предпринять другие соответствующие действия, когда есть несоответствие.
или
перебирать только те элементы корзины, которые имеют product
object.
Вы можете обновить свой for loop
следующим образом. так он будет более чистым.
for index, product in enumerate(products):
cart[product.id] = {
"product_price": product.price,
"quantity": keys[index],
"total_price": product.price * keys[index],
}
yield cart[product.id]
#OR return cart
Не стоит использовать входы widget_tweaks для форм. Он не передает значения размера и цвета должным образом, а в корзину добавляет товар без product_obj. Поэтому в данном случае лучше использовать обычный input в форме. В результате в cart_detail.html вместо:
{% render_field item.product_update_quantity_form.color value="{{item.color.color}}" type="hidden" %}
{% render_field item.product_update_quantity_form.size value="{{item.size.size}}" type="hidden" %}
используйте это:
<input type="text" value="{{item.size}}" name="size" type='hidden'>
<input type="text" value="{{item.color}}" name="color" type='hidden'>