Django cart not updating quantity - django

Hello I am working on my ecommerce django tutorial and for some reason my simple cart count is not working. Everytime I add to the cart the {{cart_item_count}} in my cart.html is 0
Also in relation to this code the book has my carts.py as cart.py but since the app is cart for some reason is doesnt like this so I renames my cart.py "carts.py" and did this
from cart import carts as cart
Does Django not allow a py file the same name as the app???
To help with the code
carts.py has a function def cart_disinct_item_count(request) which returns the count which is called in my views.py to set the variable "cart_item_count " which is displayed in my cart.html page but currently no matter what my form has is returns 0.
I didnt post the entire project code but I think I got all the related information needed.
Thanks
cart.html
{% block content %}
<h1>Cart Page Here</h1>
Cart item count: {{cart_item_count }}
{% endblock %}
cart views.py
# Create your views here.
from django.shortcuts import render_to_response
from django.template import RequestContext
from cart import carts as cart
def show_cart(request, template_name="cart/cart.html"):
cart_item_count = cart.cart_disinct_item_count(request)
page_title = 'Shopping Cart'
return render_to_response("cart/cart.html", locals(),
context_instance = RequestContext(request))
cart item model
class CartItem(models.Model):
cart_id = models.CharField(max_length=50)
date_added = models.DateTimeField(auto_now_add = True)
quantity = models.IntegerField(default = 1)
product = models.ForeignKey('catalog.Product', unique = False)
class Meta:
app_label = ''
db_table = 'cart_items'
ordering = ['date_added']
def total(self):
return self.quantity * self.product.price
def name(self):
return self.product.name
def price(self):
return self.product.price
def get_absolute_url(self):
return self.product.get_absolute_url()
def augment_quantity(self, quantity):
""" called when a POST request comes in for a Product instance already in the shopping cart """
self.quantity = self.quantity + int(quantity)
self.save()
carts.py
def get_cart_items(request):
return CartItem.objects.filter(cart_id=_cart_id(request))
#add an item to the cart
def add_to_cart(request):
postdata = request.POST.copy()
#get product slug from post data, return blank if empty
product_slug = postdata.get('product_slug', '')
#get quantity added, return 1 if empty
quantity = postdata.get('quantity', 1)
#fetch the product or return a missing page error
p = get_object_or_404(Product, slug = product_slug)
#get products in cart
cart_products = get_cart_items(request)
product_in_cart = False
#check to see if item is already in cart
for cart_item in cart_products:
if cart_item.product.id == p.id:
#update the quantity if found
cart_item.augment_quantity(quantity)
product_in_cart = True
if not product_in_cart:
#create and save a new cart item
ci = CartItem()
ci.product = p
ci.quantity = quantity
ci.cart_id = _cart_id(request)
ci.save()
#returns the total number of items in the user's cart
def cart_disinct_item_count(request):
return get_cart_items(request).count()
forms.py:
class ProductAddToCartForm(forms.Form):
quantity = forms.IntegerField(widget=forms.TextInput(attrs={'size':'2',
'value':'1', 'class':'quantity'}),
error_messages={'invalid': 'Please enter a valid quantity'},
min_value = 1)
product_slug = forms.CharField(widget = forms.HiddenInput())
#override the default __init__ so we can set the request
def __init__(self, request = None, *args, **kwargs):
self.request = request
super(ProductAddToCartForm, self).__init__(*args, **kwargs)
*EDIT**
forgot to add the view that shows the product and calls add_to_cart:
#new product view, with POST vs GET detection
def show_product(request, product_slug, template_name = "catalog/product.html"):
p = get_object_or_404(Product, slug=product_slug)
categories = p.categories.all()
page_title = p.name
meta_keywords = p.meta_keywords
meta_description = p.meta_description
#need to evaluate the HTTP method
if request.method == 'POST':
#add to cart....create the bound form
postdata = request.POST.copy()
form = ProductAddToCartForm(request, postdata)
#check if posted data is valid
if form.is_valid():
#add to cart and redirect to cart page
cart.add_to_cart(request)
# if test cookie worked, get rid of it
if request.session.test_cookie_worked():
request.session.delete_test_cookie()
url = urlresolvers.reverse('show_cart')
return HttpResponseRedirect(url)
else:
# it's a GET, create the unbound form. Note request as a kwarg
form = ProductAddToCartForm(request = request, label_suffix = ':')
#assign the hidden input the product slug
form.fields['product_slug'].widget.attrs['value'] = product_slug
#set the test cookie on our first GET request
request.session.set_test_cookie()
return render_to_response("catalog/product.html", locals(), context_instance=RequestContext(request))

Found the error :) Indentation issues (stupid python) j/k first one is mine and second one is the correct one. Gotta get use to this. Was easy to overlook to the untrained eye....
def add_to_cart(request):
postdata = request.POST.copy()
product_slug = postdata.get('product_slug', '')
quantity = postdata.get('quantity', 1)
p = get_object_or_404(Product, slug = product_slug)
cart_products = get_cart_items(request)
product_in_cart = False
for cart_item in cart_products:
if cart_item.product.id == p.id:
cart_item.augment_quantity(quantity)
product_in_cart = True
if not product_in_cart:
ci = CartItem()
ci.product = p
ci.quantity = quantity
ci.cart_id = _cart_id(request)
ci.save()
Here is the books and it works:
def add_to_cart(request):
postdata = request.POST.copy()
product_slug = postdata.get('product_slug','')
quantity = postdata.get('quantity',1)
p = get_object_or_404(Product, slug=product_slug)
cart_products = get_cart_items(request)
product_in_cart = False
for cart_item in cart_products:
if cart_item.product.id == p.id:
cart_item.augment_quantity(quantity)
product_in_cart = True
if not product_in_cart:
ci = CartItem()
ci.product = p
ci.quantity = quantity
ci.cart_id = _cart_id(request)
ci.save()

Related

Save two model (One contains foreign key) data from a single template

I'm pretty new to django and working on a blog based project where user can add ratings to specific type of review post.For example giving stars are enabled for restaurant but not for tech stores.
I have created two different form for "review" and "star" model. I want to rate the restaurant using the model named "star" and do it in same template.But I'm having difficulties to do that.
Im getting this error.
"The above exception (NOT NULL constraint failed: reviews_star.post_id_id) was the direct cause of the following exception:"
How do I get the review id that I just save with "review_form.save()".
my review model kinda looks like this (Removed other attributes which aren't related to this problem):
class Review(models.Model):
review_title = models.CharField(verbose_name='Title', max_length=100)
review_body = models.TextField()
author = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
restaurant_or_techstore = models.CharField(verbose_name='Foods or Travel',max_length=20)
slug = models.SlugField(null=False,unique=True,max_length = 300)
My rating model looks like this:
class Star(models.Model):
post_id = models.ForeignKey(Review, on_delete = models.CASCADE )
food = models.FloatField(verbose_name='Food',null=False)
service = models.FloatField(verbose_name='Food',null=False)
cleanliness = models.FloatField(verbose_name='Food',null=False)
and my view :
def CreateReview(request):
ImageFormSet = modelformset_factory(Image,form=ImageForm,extra=5)
if request.method == 'POST':
reviewForm = ReviewForm(request.POST)
formset = ImageFormSet(request.POST,request.FILES,queryset=Image.objects.none())
starsForm = StarsrForm(request.POST)
if reviewForm.is_valid() and formset.is_valid() and starsForm.is_valid():
review_form = reviewForm.save(commit=False)
review_form.author = request.user
review_form.post_or_discussion = 1
review_form.food_or_travel = 'Foods'
review_form.save()
reviewForm.save_m2m()
starsForm.save()
for form in formset.cleaned_data:
if form:
image = form['image']
photo = Image(review=review_form,image=image)
photo.save()
messages.success(request,'Image Uploaded Successfully')
return HttpResponseRedirect("/")
else:
print(reviewForm.errors, formset.errors)
else:
reviewForm = ReviewForm()
starsForm = StarsrForm()
formset = ImageFormSet(queryset=Image.objects.none())
return render(request,'reviews/review_form.html',{'reviewForm':reviewForm,'formset':formset,'starsForm':starsForm})
best try when you use ForeighKey is to use related_name attr for this field
class Star(models.Model):
post = models.ForeignKey(Review, related_name='post_review', on_delete = models.CASCADE )
after it you can refer to star from review by some_object_review.post_review.some_field_star
For you error above try reviews_star.post_id.id
Solve The problem.All I had to do is pass the review_form instance to the star form.
new view is:
if request.method == 'POST':
reviewForm = ReviewForm(request.POST)
formset = ImageFormSet(request.POST,request.FILES,queryset=Image.objects.none())
starsForm = StarsrForm(request.POST)
if reviewForm.is_valid() and formset.is_valid() and starsForm.is_valid():
review_form = reviewForm.save(commit=False)
review_form.author = request.user
review_form.post_or_discussion = 1
review_form.food_or_travel = 'Foods'
review_form.save()
reviewForm.save_m2m()
instance = review_form
print(instance.id)
star_form = starsForm.save(commit=False)
star_form.post_id = instance
star_form.save()
for form in formset.cleaned_data:
if form:
image = form['image']
photo = Image(review=review_form,image=image)
photo.save()
messages.success(request,'Image Uploaded Successfully')
return HttpResponseRedirect("/")
else:
print(reviewForm.errors, formset.errors)
else:
reviewForm = ReviewForm()
starsForm = StarsrForm()
formset = ImageFormSet(queryset=Image.objects.none())
return render(request,'reviews/review_form.html',{'reviewForm':reviewForm,'formset':formset,'starsForm':starsForm})

Django sessions save only one object

I am building online store and want to use sessions to allow anonymous users to add items to carts before logging in .
trying to save the added items in session as a list but it saves only last object added to list
def cart(request):
user_id = request.user.id
cart_items =Cart.objects.all().filter(user_id=user_id, is_ordered=False)
ids = request.session.get('cart_id')
if ids:
for cart_id in ids:
if request.user.is_authenticated:
cart = Cart.objects.get(id=cart_id)
if cart:
cart.user = request.user
cart.save()
# get total prices
prices = []
for item in cart_items:
prices.append(item.item.price * item.qty)
total_price = sum(prices)
context = {
'items': cart_items,
'total_price': total_price
}
request.session['prices'] = prices
return render(request, 'cart.html', context)
def add_to_cart(request):
if request.user.is_authenticated:
item = Item.objects.get(pk=request.POST['item_id'])
cart = Cart.objects.filter(
user=request.user, item=item, is_ordered=False).first()
if cart:
cart.qty += 1
cart.save()
messages.success(request, 'item added to cart!')
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
else:
cart = Cart(user=request.user, item=item)
cart.save()
messages.success(request, 'item added to cart!')
else:
carts_ids = []
session_cart = request.session.get('cart_id')
item = Item.objects.get(pk=request.POST['item_id'])
if session_cart :
for cart in session_cart:
carts = Cart.objects.all().filter(id=cart, item=item)
if carts:
cart = carts.filter(item=item)[0]
print(cart.qty)
cart.qty += 1
cart.save()
else:
print('new cart')
new_cart = Cart(item=item)
new_cart.save()
carts_ids.append(new_cart.id)
request.session['cart_id'] = carts_ids
else:
print('new session')
new_cart = Cart(item=item)
new_cart.save()
carts_ids.append(new_cart.id)
request.session['cart_id'] = carts_ids
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
here are my models.py
and the full code on github i am only working now on these to views (cart and add_to_cart)
appriciate any help to get over this and to simplify my code :)
The idea would be that you save the whole cart in sessions so that you can reload it when the same user comes back to your website. So your code should look like this:
prices = []
itemlist=[]
for item in cart_items:
prices.append(item.item.price * item.qty)
itemlist.append(item.item, item.qty)
total_price = sum(prices)
context = {
'items': cart_items,
'total_price': total_price
}
request.session['items'] = itemlist
End then you also will need to read out this list.

Django: Cart matching query does not exist

I've a form that creates a Cart with the text "Random" in a character field, if there is not a Cart object created. This is only to get this recently object's id if it is not already created.
cart = Cart.objects.get(id=cart_id)
I get an error saying that this query generates an error, however I can see the value of cart_id as a cookie so the Query should execute without problem. But it doesn't according to the error message.
Exception Type: DoesNotExist
Exception Value: Cart matching query does not exist.
As you can see in my view, I'm using this:
cart_id = self.request.COOKIES.get('cart_id')
if not cart_id:
cart = Cart.objects.create(cart_id="Random")
cart_id = cart.id
cart = Cart.objects.get(id=cart_id)
To get the cookie cart_id if it does not exist I created a Cart object with the Random text, only to get it's ID.
Why I'm getting the error?
View.py:
class StepOneView(FormView):
form_class = StepOneForm
template_name = 'shop/medidas-cantidades.html'
success_url = 'subir-arte'
def get_initial(self):
# pre-populate form if someone goes back and forth between forms
initial = super(StepOneView, self).get_initial()
initial['size'] = self.request.session.get('size', None)
initial['quantity'] = self.request.session.get('quantity', None)
initial['product'] = Product.objects.get(
category__slug=self.kwargs['c_slug'],
slug=self.kwargs['product_slug']
)
return initial
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['product'] = Product.objects.get(
category__slug=self.kwargs['c_slug'],
slug=self.kwargs['product_slug']
)
return context
def form_invalid(self, form):
print('Step one: form is NOT valid')
def form_valid(self, form):
cart_id = self.request.COOKIES.get('cart_id')
if not cart_id:
cart = Cart.objects.create(cart_id="Random")
cart_id = cart.id
cart = Cart.objects.get(id=cart_id)
item = CartItem.objects.create(
size=form.cleaned_data.get('size'),
quantity=form.cleaned_data.get('quantity'),
product=Product.objects.get(
category__slug=self.kwargs['c_slug'],
slug=self.kwargs['product_slug']
),
cart=cart
)
response = HttpResponseRedirect(self.get_success_url())
response.set_cookie("cart_id", cart_id)
response.set_cookie("item_id", item.id)
return response
models.py:
class Cart(models.Model):
cart_id = models.CharField(max_length=100)
date_added = models.DateField(auto_now_add=True)
class Meta:
db_table = 'Cart'
ordering = ['date_added']
def __str__(self):
return str(self.id)
Try this:
def form_valid(self, form):
cart_id = self.request.COOKIES.get('cart_id')
if cart_id:
try:
cart = Cart.objects.get(id=cart_id)
except ObjectDoesNotExist:
# supplied ID doesn't match a Cart from your BD
cart = Cart.objects.create(cart_id="Random")
else:
cart = Cart.objects.create(cart_id="Random")
No need to create the Cart objects, just to get its ID and they hit the DB again to retrieve the same instance.
The idea is:
Do you have cart_id on session cookies? If so, try to get the Cart object based on it.
If that fails, it means that the supplied cart_id doesn't match an object from your DB
If no cart_id on session cookies, then simply create your new Cart object.

Django how to handle two forms with one-to-many relationship between models in one post

For the sake of simplicity, here is a short version of the issue I am dealing with. Say you have two models, Product and ProductPricing, with Product having many ProductPricing instances and a product pricing is related to only one product (one-to-many relationship). The model's clean method:
models.py:
class ProductPricing(models.Model):
product = models.ForeignKey(Product, related_name = 'prices', verbose_name = 'Price', blank = True, null = True)
customer = models.ForeignKey(Customer, related_name = 'customer_pricings', blank = True, null = True)
...
def clean(self):
if not self.product and not self.customer:
raise ValidationError(_('Pricing requires either a product or a customer. None is provided') )
forms.py:
class ProductPricingAddonForm(ModelForm):
class Meta:
model = ProductPricing
fields = ['UOM', 'cost_per_UOM', 'product']
Now, In my UpdateView, I am doing something like this:
class UpdateProductView(View):
template_name = 'product.html'
product_form = ProductForm
pricing_form = ProductPricingAddonForm
def get(self, request, *args, **kwargs):
product_id = kwargs.get('pk', None)
product = Product.objects.get(id = product_id)
product_form = self.product_form()
pricing_form = self.pricing_form()
c = {}
c['product_form'] = product_form
c['pricing_form'] = pricing_form
return render(request, self.template_name, c)
def post(self, request, *args, **kwargs):
''' To update both the models in one go'''
product_id = kwargs.get('pk', None)
product = Product.objects.get(id = product_id)
product_pricing = ProductPricing.objects.filter(product = product)
product_form = self.product_form(request.POST if any(request.POST) else None, request.FILES or None, instance = product)
if product_form.is_valid():
product_form.save()
product_form_validity_flag = True
updated_product = Product.objects.get(id = product_id)
else:
product_form_errors = product_form.errors
...
# if no existing pricing specified for the product:
pricing_form = self.pricing_form(request.POST if any(request.POST) else None, request.FILES or None)
# Since a productpricing instance is required to have product or customer, the updated_product should somehow be attached to the form:
pricing_form['product'] = updated_product # <-- Error here
if pricing_form.is_valid():
pricing_form.save()
pricing_form_validity_flag
else:
pricing_form_errors = pricing_form.errors
if product_form_validity_flag and pricing_form_validity_flag:
return reverse(...)
else:
c = {'product_form': product_form,
'pricing_form': pricing_form }
return render(request, self.template_name, c)
The problem is when submitting the form, I get the error message that 'ProductPricingAddonForm' object does not support item assignment. Note, if I do not provide the product to the productpricing form somehow, the validation error defined in the clean method of the ProductPricing model will be thrown at me.
So, my question is how to attach the parent instance (product) to the child instance (productpricing) in the same post method (i.e. not creating a productpricing in a different page)?

django remove item from shopping cart

I am following the shopping cart example from django ecommerce book and I am facing an issue when it comes to remove items from shopping cart, I cannot get to remove single items from the session.
This is part of my cart.py
class Cart(object):
class Item(object):
def __init__(self, itemid, product, quantity=1, price=0):
self.itemid = itemid
self.product = product
self.quantity = quantity
self.price = price
self.totalprice = price * quantity
def remove_item(self, itemid):
self.items = filter(lambda x: x.itemid != itemid, self.items)
views.py
def remove_from_cart(request, cart_item_id):
cart = get_shopping_cart(request)
cart.remove_item(int(cart_item_id))
update_shopping_cart(request, cart)
cart = get_shopping_cart(request)
ctx = {'cart': cart}
I am using Python 2.7 and Django 1.5. Is anything wrong with the definition of the remove_item method? I have tried several options but coudn't make it work
Any hint would be appreciated.
EDIT: my update_shopping_cart method is:
def update_shopping_cart(request, cart):
request.session['cart'] = cart
The view where the cart allows remove items:
def shopping_cart(request):
cart = get_shopping_cart(request)
if request.method == 'POST':
cart = get_shopping_cart(request)
cart.remove_item(cart_item_id)
update_shopping_cart(request, cart)
cart = get_shopping_cart(request)
ctx = {'cart': cart}
render_to_response(...)
I think there are two issues:
Did you set SESSION_SAVE_EVERY_REQUEST=True?
In the method
def remove_from_cart(request, cart_item_id):
cart = get_shopping_cart(request)
cart.remove_item(int(cart_item_id))
update_shopping_cart(request, cart)
cart = get_shopping_cart(request)
ctx = {'cart': cart}
you are actually fetching your data, then changing it to then fetch again the old data. Why do you do the second cart = get_shopping_cart(request)? That probably overwrites the changes you have just made.
Adapt the code as follows
def remove_from_cart(request, cart_item_id):
cart = get_shopping_cart(request)
cart.remove_item(int(cart_item_id))
update_shopping_cart(request, cart)
# cart = get_shopping_cart(request)
ctx = {'cart': cart}
and adapt your update_shopping_cart(request, cart) as Greg suggests to
def update_shopping_cart(request, cart):
request.session['cart'] = cart
request.session.modified = True
Does adding this line help?
def update_shopping_cart(request, cart):
request.session['cart'] = cart
request.session.modified = True