I have a problem with an order view of my Django application.
The whole error:
Value Error: Cannot assign "SimpleLazyObject: User: wad2": "OrderItem.order"
must be a "Order" instance.
This is the order view:
#login_required
def my_order(request):
user = request.user
context = {}
cart = Cart(request)
if request.method == 'POST':
form = OrderCreateForm(request.POST or None, instance=user)
if form.is_valid():
order = form.save()
for item in cart:
OrderItem.objects.create(
order=order,
product=item['product'],
price=item['price'],
quantity=item['quantity'])
cart.clear()
context['order'] = order
else:
form = OrderCreateForm()
context['form']=form
return render(request, 'iFood/my-order.html', context)
The form:
class OrderCreateForm(forms.ModelForm):
class Meta:
model = Order
fields = ('created’,)
The models for OrderItem and Order:
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.PROTECT, related_name='orders')
created = models.DateField(default=datetime.date.today())
def __str__(self):
return 'Order {}'.format(self.id)
def get_total_cost(self):
return sum(item.get_cost() for item in self.items.all())
class OrderItem(models.Model):
order = models.ForeignKey(Order, related_name='items', on_delete=models.CASCADE)
product = models.ForeignKey(Product, related_name='order_items', on_delete=models.CASCADE)
price = models.DecimalField(max_digits=10, decimal_places=2)
quantity = models.PositiveIntegerField(default=1)
def __str__(self):
return '{}'.format(self.id)
def get_cost(self):
return self.price * self.quantity
The reason I'm using the form is just to confirm the user wants to create the order. I know there's something wrong with the way I create the OrderItem and reference order there but I don't know how to resolve it.
You use instance in a model form when you want to edit an existing instance. If the model form is for Order, then the instance must be an order. You are passing a User instance, so you get the error.
In your case, you are creating a new order so you don't have to pass an instance. You can save the form with commit=False, and then set the user:
if request.method == 'POST':
form = OrderCreateForm(request.POST or None)
if form.is_valid():
order = form.save(commit=False)
order.user = request.user
order.save()
...
Since you don't get the user to fill in any fields in the form, you could get rid of the form completely. You can still check request.method, and that way you will only create the order for POST requests.
if request.method == 'POST':
order = Order.objects.create(user=request.user)
...
Related
I have this model:
class OrderProduct(models.Model):
order = models.ForeignKey(to=Order, on_delete=models.CASCADE)
product = models.ForeignKey(to=Product, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField(default=1)
price_paid = models.DecimalField(max_digits=5, decimal_places=2)
#property
def total_value(self):
return self.price_paid * self.quantity
def __str__(self):
return f"{self.order.id} / {self.order.user} // {self.product.name} / {self.quantity} ks"
def save(self, *args, **kwargs):
self.price_paid = self.product.price
super(OrderProduct, self).save(*args, **kwargs)
This is my form:
class ChangeOrderProductForm(ModelForm):
class Meta:
model = OrderProduct
fields = ('product',)
This is my view:
def change_orderproduct(request, order_id: int):
set_session_cookie_restraunt_status(request)
if not check_if_user_has_permission(request, 'kitchen.change_orderproduct'):
messages.warning(request, 'Unauthorized to perform this action.')
return redirect("index")
ordered_products = OrderProduct.objects.filter(order__pk=order_id).first()
order = Order.objects.filter(pk=order_id).first()
if not order:
messages.info(request, "Given order does not exist.")
return redirect(request.META.get('HTTP_REFERER', index))
if request.method == "GET":
form = ChangeOrderProductForm()
context = {
"form": form,
"order": order
}
return render(request, "kitchen/change_orderproduct.html", context=context)
if request.method == "POST":
form = ChangeOrderProductForm(data=request.POST, instance=ordered_products)
if form.is_valid():
form.save()
messages.success(request, f"Products in order {order.pk} were successfully changed.")
return redirect(request.META.get('HTTP_REFERER', all_orders_view))
The code above works, but it only allows you to choose 1 Product in the ChangeOrderProductForm view:
However, there are multiple products for almost all Order instances.
How do I make it to:
Show all the possible Product instances to choose from in the form?
Make the already existing ForeignKeys assigned to OrderProduct as the pre-selected ones?
Basically my idea is, that if a user wants to modify the Products assigned to OrderProduct he will be able to choose from the whole list of the existing Products while also he will be able to see the already existing Products which are already linked to the OrderProduct. The user will then be able to select which Products he wants to either unlink or link to the given OrderProduct.
Thank you
When I do this exactly as provided below, a shipping address object is created without the customer assigned in the shipping address foreignkey field, I can add it from the admin panel manually but I'm not able to make it work through code, idk what I'm doing wrong, please help!
**models.py**
class Customer(models.Model):
user = models.OneToOneField(CustomUser, on_delete=models.CASCADE, blank=True, null=True)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.EmailField(max_length=150)
class ShippingAddress(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.SET_NULL, blank=True, null=True)
order = models.ForeignKey(Order, on_delete=models.SET_NULL, blank=True, null=True)
address_one = models.CharField(max_length=200)
address_two = models.CharField(max_length=200)
...
**views.py**
def checkout(request):
if request.user.is_authenticated:
customer = request.user.customer
order, created = Order.objects.get_or_create(customer=customer, complete=False)
items = order.orderitem_set.all()
else:
items = []
order = {'get_cart_total': 0, 'get_cart_items': 0}
if request.method == 'POST':
form = ShippingForm(request.POST)
if form.is_valid():
#how do I get the customer to get added in the foreignkey field for the shipping address model
form.save()
return redirect('store:checkout_shipping')
else:
form = ShippingForm()
else:
form = ShippingForm()
context = {"items": items, "order": order, "form": form}
return render(request, 'store/checkout.html', context)
In response to your comment #how do I get the customer to get added... etc, in the case that your ShippingForm() points to your ShippingAddress model, or at least something with a customer foreign key field, you may need to do something like this:
def checkout(request):
if request.user.is_authenticated:
customer = request.user.customer
order, created = Order.objects.get_or_create(customer=customer, complete=False)
items = order.orderitem_set.all()
else:
items = []
order = {'get_cart_total': 0, 'get_cart_items': 0}
if request.method == 'POST':
form = ShippingForm(request.POST)
if form.is_valid():
new_shipment = form.save(commit=False)
new_shipment.customer = customer
new_shipment.save()
return redirect('store:checkout_shipping')
else:
form = ShippingForm()
else:
form = ShippingForm()
context = {"items": items, "order": order, "form": form}
return render(request, 'store/checkout.html', context)
Using commit=False on the form.save() will allow you to subsequently modify other fields, in this case, by adding the customer relation, then saving it. More information here in the Django documentation. Salient quote:
This save() method accepts an optional commit keyword argument, which
accepts either True or False. If you call save() with commit=False,
then it will return an object that hasn’t yet been saved to the
database. In this case, it’s up to you to call save() on the resulting
model instance. This is useful if you want to do custom processing on
the object before saving it, or if you want to use one of the
specialized model saving options. commit is True by default.
"Custom processing" in this case is the creation of the foreign key relationship to the model instance (customer).
models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Customer(models.Model):
user = models.OneToOneField(User,null=True,blank=True,on_delete=models.CASCADE)
name = models.CharField(max_length=200,null=True)
## phone = models.IntegerField(null=True)
email = models.EmailField(max_length=250)
profile_pic = models.ImageField(default='default_pic.png',null=True,blank=True)
date_created = models.DateTimeField(auto_now_add=True,null=True)
def __str__(self):
return self.name
class Task(models.Model):
customer = models.ForeignKey(Customer,on_delete=models.CASCADE,null=True)
title = models.CharField(max_length=200)
description = models.TextField(null=True,blank=True)
complete = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
class Meta:
ordering = ['complete']
views.py
#login_required(login_url='login')
def taskCreate(request):
if request.method == 'POST':
form = TaskForm(request.POST or None)
if form.is_valid():
form.instance.customer = request.user
form.save()
return redirect('tasks')
else:
form = TaskForm()
context = {'form':form}
return render(request,'todo_list/task_create.html',context)
Error:
ValueError at /create_task/
Cannot assign "<SimpleLazyObject: <User: Dominic>>": "Task.customer" must be a "Customer" instance.
I am trying to link the username in the user account to be shown on the model Task.customer that represents the post is created by that user. May I ask any methods could be done in order to specify the customer in the model Task? Also I do not understand the error message in detail because my admin panel already has the current username in the Customer model. However if I used request.user.customer the username does not show up instead returning None so how to solve this issue?
I don't know form.instance.customer = request.user
but I think I understood what you meant and the below code does the same thing
#login_required(login_url='login')
def taskCreate(request):
if request.method == 'POST':
form = TaskForm(request.POST or None)
if form.is_valid():
t = form.save(commit = False)
t.customer = request.user # assigning current user to created task.customer
t.save()
return redirect('tasks')
else:
form = TaskForm()
context = {'form':form}
return render(request,'todo_list/task_create.html',context)
if the code is still not working then try changing your line
customer = models.ForeignKey(Customer,on_delete=models.CASCADE,null=True) to
customer = models.ForeignKey(User,on_delete=models.CASCADE,null=True) in your models.py
The error comes from the following snippet
form.instance.customer = request.user
request.user is not a Customer instance, you can try extracting the information from request.user and create a Customer object from it and then assign it back
I currently have models shown below
class Product(models.Model):
name = models.CharField(max_length=200, null=True)
stock = models.CharField(max_length=200, null=True)
class Order(models.Model):
product = models.ForeignKey(Product, null=True, on_delete = models.SET_NULL)
As you can see the order model has the product model as a foreign key.
When I create or update instances based upon on the order model, I would like to decrement one value from the stock field in the products model.
See below for the my views for both when creating and updating instances.
#login_required
def newOrder(request):
form = CreateOrderForm()
if request.method == 'POST':
form = CreateOrderForm(request.POST or None)
if form.is_valid():
form.save()
return redirect('customer_order_list')
return render(request, 'accounts/new_order.html', {'form': form})
#login_required
def editOrder(request, pk):
order = Order.objects.get(id=pk)
sho = Order.objects.all().values_list('date_created')
order_date = sho.filter(id=pk)
form = CreateOrderForm(instance=order)
if request.method == "POST":
form = CreateOrderForm(request.POST, instance=order)
if form.is_valid():
form.save()
return redirect('customer_order_list')
return render(request, 'accounts/edit_order.html', {'form': form, 'order_date': order_date})
I am aware that using the a similar example below will need to be implemented, however I will have to use the primary key of that particular instance.
with transaction.atomic():
product = (
Product.objects
.select_for_update()
.get(id=1)
)
product.inventory -= 1
product.save()
However this example will not apply for when creating instance based on the order form.
How can one implement this?
If I'm not mistaken, you're trying to update Product.stock when an Order is made. You can use signals for this. For example:
from django.db.models.signals import post_save
#receiver(post_save, sender=Order)
def update_stock(sender, instance, created, **kwargs):
if created:
instance.product.stock = instance.product.stock - 1
instance.product.save()
Trying to implement a file upload for a user profile page. I am recieving the following error:
null value in column "user_id" violates not-null constraint
DETAIL: Failing row contains (35,
profile/{now:%Y/%m/YmdHMSext_xg2iZ6M, null, null).
I've read that it probably has something to do with the User_ID, I tried passing form.user = request.user, but that didn't work. There are also two nulls, not just one.
Models.py
class User(AbstractUser):
# First Name and Last Name do not cover name patterns
# around the globe.
name = models.CharField(_('Name of User'), blank=True,
max_length=255)
#accepted_terms_of_service = models.Booleanfield()
def __str__(self):
return self.username
def get_absolute_url(self):
return reverse('users:detail', kwargs={'username':
self.username})
# Profile Image
def upload_to(instance, filename):
now = timezone_now()
base, ext = os.path.splitext(filename)
ext = ext.lower()
return "profile/{now:%Y/%m/%Y%m%d%H%M%S}{ext}"
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,
on_delete='CASCADE', related_name='user_profile')
school = models.CharField(max_length=30, null=True, blank=True)
image = models.ImageField(_("Picture"), upload_to=upload_to,
null=True, blank=True)
def __str__(self):
return self.user.username
views.py
#login_required
def add_image(request):
form = ProfileImageForm()
#form.user = request.user
if request.method == "POST":
form = ProfileImageForm(data=request.POST, files=request.FILES)
if form.is_valid():
form.save()
return redirect('userPage')
else:
return render(request, "users/user_image_form.html", {"form": form
})
forms.py
class ProfileImageForm(forms.ModelForm):
class Meta:
model = Profile
fields = ["image"]
This is because in your Profile model you add user column as ForeignKey which enforce to NOT NULL so the error throw.
To solve this you need to modify add_image method something like this
#login_required
def add_image(request):
form = ProfileImageForm()
#form.user = request.user
if request.method == "POST":
form = ProfileImageForm(data=request.POST, files=request.FILES)
if form.is_valid():
form = form.save(commit=False) # change is here
form.user=request.user.pk # change is here
form.save()
return redirect('userPage')
else:
return render(request, "users/user_image_form.html", {"form": form
The request.user.pk value get if you are logged in. But if you are logged in you need to assisn form.user = your_specified_id which id exists in User table.
If your case is, you are admin and you need to add an image to other users, so that you need to pass the user id in your add_image method.
Add in ProfileImageForm.py
add user in field list
I think its not necessary to have both Profile Model and Custom User Model. Because, as you are customizing the User model already, why not put Profile model's fields to User model as well. You can approach like this:
# model
def upload_to(instance, filename):
now = timezone_now()
base, ext = os.path.splitext(filename)
ext = ext.lower()
return "profile/{now:%Y/%m/%Y%m%d%H%M%S}{ext}"
class User(AbstractUser):
name = models.CharField(_('Name of User'), blank=True,
max_length=255)
school = models.CharField(max_length=30, null=True, blank=True)
image = models.ImageField(_("Picture"), upload_to=upload_to,
null=True, blank=True)
def __str__(self):
return self.username
def get_absolute_url(self):
return reverse('users:detail', kwargs={'username':
self.username})
# views
#login_required
def add_image(request):
form = ProfileImageForm(data=request.POST or None, file=request.FILES or None, instance=request.user)
if request.method == "POST":
if form.is_valid():
form.save()
return redirect('userPage')
return render(request, "users/user_image_form.html", {"form": form
})
# forms.py
class ProfileImageForm(forms.ModelForm):
class Meta:
model = User
fields = ["image"]
Update
You can create a post_save signal, which will create a Profile Instance after each User is created.
def create_user_profile(sender, instance, created, **kwargs):
if created:
profile = Profile(user=instance)
profile.save()
post_save.connect(create_user_profile,
sender=User,
dispatch_uid="profilecreation-signal")
Now in your form, you can directly pass this Profile instance:
#login_required
def add_image(request):
form = ProfileImageForm(data=request.POST, files=request.FILES, instance=request.user.profile)
if request.method == "POST":
if form.is_valid():
form.save()
return redirect('userPage')
else:
return render(request, "users/user_image_form.html", {"form": form
})
For existing user, you can create Profile from shell:
for user in User.objects.all():
Profile.objects.get_or_create(user=user)