modifying request session in django - django

this code is from djagno 2 by example book
which is very helpful by the way. the question here is how we managed to modify the request session although we didn't modify it except in save method ((( and it's on the session of the cart which in the first line after init)))
what i see is that we have made a copy from the session dictionary at this line
self.session = request.session
class Cart(object):
def __init__(self, request):
self.session = request.session
cart = self.session.get(settings.CART_SESSION_ID)
if not cart:
cart = self.session[settings.CART_SESSION_ID] = {}
self.cart = cart
def add(self, product, quantity=1, update_quantity=False):
'''
Add a product to the cart or update it is quantity
'''
product_id = product.id
if not product_id in self.cart:
self.cart[product_id] = {'quantity': 0,
'price': str(product.price)}
if update_quantity:
self.cart[product_id]['quantity'] = quantity
else:
self.cart[product_id]['quantity'] += quantity
self.save()
def save(self):
self.session['modified'] = True
for more clarification you can find the whole code of the project here
code
i managed to see the user session as I passed it from the context to the template and it's already modified

This line does not make a copy:
self.session = request.session
The session in the Cart instance is the request session. self.cart is a dictionary in that session, so all changes to it are written directly to the session. Then when save is called the modified flag is set.

Related

Setting user kwargs as field vales in Django Form with Class-based View

I have a Django application (1.11) to track referrals (referred by a user). I want to pass the id of the authenticated user to the ModelForm 'referrer' field (and since it's from the current logged in user the field shouldn't be editable).
class Referral(models.Model):
name = models.CharField(max_length=100)
referrer = models.ForeignKey('users.User', on_delete=models.SET_NULL, related_name='referrals', null=True, blank=True)
View:
class ReferralFormView(FormView):
form_class = ReferralForm
template_name = "refer.html"
success_url = reverse_lazy('thanks')
def get(self, request):
if request.user.is_authenticated():
return super(ReferralFormView, self).get(request)
else:
return redirect('login')
def get_form_kwargs(self):
user = self.request.user
form_kwargs = super(ReferralFormView, self).get_form_kwargs()
form_kwargs['referrer'] = user.id
return form_kwargs
def form_valid(self,form):
...
form.save()
return super(ReferralFormView, self).form_valid(form)
I override get_form_kwargs in the view, then modify form init
class ReferralForm(forms.ModelForm):
class Meta:
model = Referral
def __init__(self, *args, **kwargs):
referrer = kwargs.pop('referrer', None)
super(ReferralForm, self).__init__(*args, **kwargs)
self.fields['referrer'].disabled = True
self.fields['referrer'].queryset = User.objects.filter(id=referrer)
However all I see is a blank referrer field, what am I missing to make the user the value of that field (which can't be edited)? I also tried self.fields['referrer'].initial = User.objects.filter(id=referrer). I don't want the user to have to select their own username from a queryset of one.
I can print a <QuerySet [<User: username>]> for user = User.objects.filter(id=referrer), so why isn't it setting that user as the field value?
Update: I can assign the user value with
self.fields['referrer'].initial = User.objects.filter(id=referrer).first()
self.fields['referrer'].disabled = True
However, on form submit Referral obj is not saving with the referrer field value (that value's still blank)
thanks
what I needed to do was select the user obj in the queryset:
self.fields['referrer'].initial = User.objects.filter(id=referrer).first()
but using self.fields['referrer'].disabled = True disabled the field so I was still getting a blank value on submit (disabled doesn't do what the docs say it does). Using self.fields['referrer'].initial = User.objects.filter(id=referrer).first() with that field set as readonly allows the form to submit with the initial value

Object of type Cart is not JSON serializable

I have a products model and a cart model. There is a ManyToMany relation that I put in cart model.
I am trying to return the object of cart model along with the context dictionary but it is giving this error "Object of type Cart is not JSON serializable".
I am using Django version '3.0.7'.
I am following Justing Michael's Tutorial,He is using lower Django version and its working on that.
Did Django change this thing?
class CartManager(models.Manager):
def new_or_get(self,request):
cart_id = request.session.get('cart_id')
qs = self.get_queryset().filter(id = cart_id)
if qs.count() == 1:
new_obj = False
cart_obj = qs.first()
if request.user.is_authenticated and cart_obj.user is None:
cart_obj.user = request.user
cart_obj.save()
else:
cart_obj = Cart.objects.new_cart(user = request.user)
new_obj = True
request.session['cart_id'] = cart_obj
return cart_obj,new_obj
def new_cart(self,user=None):
user_obj = None
if user is not None:
if user.is_authenticated:
user_obj = user
return self.model.objects.create(user=user_obj)
and the model actually is:
class Cart(models.Model):
user = models.ForeignKey(User,on_delete = models.CASCADE,null=True,blank=True)
products = models.ManyToManyField(Product,blank=True)
#more fields
and views.py is:
def cart_home(request):
cart_obj,new_obj = Cart.objects.new_or_get(request)
context={'cart':cart_obj}
return render(request,'carts/cart_home.html',context)
You can't send a Python class instance (such as a Model class instance) as a render data, you must send a json like object (a dict in Python).
So what you need is to convert cart_obj to a dict so it can be sent to the frontend through a render. The easiest way to do it with Django is to use a serializer (In your case, the best option is a ModelSerializer).
def cart_home(request):
cart_obj,new_obj = Cart.objects.new_or_get(request)
cart_obj_dict = CartSerializer(cart_obj).data
context={'cart':cart_obj_dict}
return render(request,'carts/cart_home.html',context)
I am sorry to disturb you guys,I had made an idiotic mistake:
request.session['cart_id'] = cart_obj
I was trying to assign cart object to session variable which was a typo ,instead in reality I should have done this:
request.session['cart_id'] = cart_obj.id
Credits: minglyu

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 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

Django cart not updating quantity

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()