Reputation: 263
I believe I understand where my issue comes from I am just not sure how to solve it. The issue is that I am trying to save a review for a product but in the select field of the product the items which are displayed are ones they have purchased. I accomplished this by overriding the init method in my form. My issue is with this exact query it doesn't give me the product, it gives me the string that is queried (due to the str in my model). Therefore when I try to add the review to the product this error is getting thrown. My issue is I do not know how I can get the product exactly because in order to display what I want to display in the select field I have to give it that specific query. Any help is greatly appreciated
form
class ReviewForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('user')
super(ReviewForm, self).__init__(*args, **kwargs)
self.fields['product'].queryset = ShoppingCartOrderItem.objects.filter(user=self.request, ordered=True)
# for item in self.fields['product'].queryset:
# print(item.item)
# print(self.fields['product'].queryset)
class Meta:
model = Review
exclude = ['user', 'date']
widgets = {
'product': forms.Select(attrs={
'class': 'form-control'
}),
'rating': forms.NumberInput(attrs={
'class': 'form-control',
'placeholder': '5.0..'
}),
'comment': forms.Textarea(attrs={
'class': 'form-control'
})
}
view
class MyOrdersView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs):
try:
order = ShoppingCartOrder.objects.filter(user=self.request.user, ordered=True)
form = ReviewForm(user=self.request.user)
context = {
'object': order,
'form': form
}
return render(request, 'my_site/my_orders.html', context)
except ObjectDoesNotExist:
messages.warning(request, 'You do not have any orders')
return redirect('all_products')
def post (self, request, *args, **kwargs):
form = ReviewForm(request.POST, user=self.request.user)
if form.is_valid():
test = form.save(commit=False)
test.user = request.user
test.save()
return redirect('starting_page')
return redirect('all_products')
models in question:
class Review(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.SET_NULL, null=True)
rating = models.DecimalField(max_digits=2, decimal_places=1, validators=[MinValueValidator(Decimal('0.1'))])
comment = models.TextField(validators=[MinLengthValidator(10)])
date = models.DateField(auto_now=True)
def __str__(self):
return str(self.user)
class ShoppingCartOrderItem(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
ordered = models.BooleanField(default=False)
item = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.IntegerField(default=1)
def __str__(self):
return f'{self.quantity} of {self.item.name}'
def get_total_order_price(self):
return self.quantity * self.item.price
def get_total_discount_item_price(self):
return self.quantity * self.item.discount_price
def get_final_price(self):
if self.item.discount_price:
return self.get_total_discount_item_price()
return self.get_total_order_price()
class Meta:
verbose_name_plural = 'Order Item(link between)'
In my form the options in the select field are based off of this query self.fields['product'].queryset = ShoppingCartOrderItem.objects.filter(user=self.request, ordered=True). Based on the model it returns the string {self.quantity} of {self.item.name}. I believe this is where my issue is but to be honest I could be completely wrong, that is just my best guess haha.
Added a picture to try to make it more clear what I mean because my explanations are not the best
edit: full traceback
Environment:
Request Method: POST
Request URL: http://localhost:8000/my-orders/
Django Version: 3.2.4
Python Version: 3.9.1
Installed Applications:
['my_site',
'django_countries',
'crispy_forms',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles']
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']
Traceback (most recent call last):
File "C:\Users\Matthew\Desktop\VScode\e_com\.venv\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
response = get_response(request)
File "C:\Users\Matthew\Desktop\VScode\e_com\.venv\lib\site-packages\django\core\handlers\base.py", line 181, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\Matthew\Desktop\VScode\e_com\.venv\lib\site-packages\django\views\generic\base.py", line 70, in view
return self.dispatch(request, *args, **kwargs)
File "C:\Users\Matthew\Desktop\VScode\e_com\.venv\lib\site-packages\django\contrib\auth\mixins.py", line 71, in dispatch
return super().dispatch(request, *args, **kwargs)
File "C:\Users\Matthew\Desktop\VScode\e_com\.venv\lib\site-packages\django\views\generic\base.py", line 98, in dispatch
return handler(request, *args, **kwargs)
File "C:\Users\Matthew\Desktop\VScode\e_com\my_site\views.py", line 349, in post
if form.is_valid():
File "C:\Users\Matthew\Desktop\VScode\e_com\.venv\lib\site-packages\django\forms\forms.py", line 175, in is_valid
return self.is_bound and not self.errors
File "C:\Users\Matthew\Desktop\VScode\e_com\.venv\lib\site-packages\django\forms\forms.py", line 170, in errors
self.full_clean()
File "C:\Users\Matthew\Desktop\VScode\e_com\.venv\lib\site-packages\django\forms\forms.py", line 374, in full_clean
self._post_clean()
File "C:\Users\Matthew\Desktop\VScode\e_com\.venv\lib\site-packages\django\forms\models.py", line 408, in _post_clean
self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude)
File "C:\Users\Matthew\Desktop\VScode\e_com\.venv\lib\site-packages\django\forms\models.py", line 63, in construct_instance
f.save_form_data(instance, cleaned_data[f.name])
File "C:\Users\Matthew\Desktop\VScode\e_com\.venv\lib\site-packages\django\db\models\fields\__init__.py", line 910, in save_form_data
setattr(instance, self.name, data)
File "C:\Users\Matthew\Desktop\VScode\e_com\.venv\lib\site-packages\django\db\models\fields\related_descriptors.py", line 215, in __set__
raise ValueError(
Exception Type: ValueError at /my-orders/
Exception Value: Cannot assign "<ShoppingCartOrderItem: 1 of Bayard defends a bridge over the Garigliano, 1505>": "Review.product" must be a "Product" instance.
Upvotes: 1
Views: 5734
Reputation: 12859
The ReviewForm
creates a queryset of ShoppingCartOrderItem
objects for the product
field. So you're using the wrong model for that field. It needs to be a queryset of the Product
model
Exception Value: Cannot assign "<ShoppingCartOrderItem: 1 of Bayard defends a bridge over the Garigliano, 1505>": "Review.product" must be a "Product" instance.
The error here is telling you that you can't assign that ShoppingCartOrderItem
that you've selected to Review.product
, because it's not a Product
.
The item
of a ShoppingCartOrderItem
is a product, so you can figure out what products a person has ordered by doing something like;
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
# First get a list of the product IDs from the `ShoppingCartOrderItem`
# objects associated with the user passed to the form.
ordered_product_ids = ShoppingCartOrderItem.objects.filter(user=user).values_list('item_id', flat=True)
# Then filter the products by the ordered product IDs so
# they can only select from the products which they ordered.
self.fields['product'].queryset = Product.objects.filter(id__in=ordered_product_ids)
Upvotes: 2