Solution for how to fetch all the item of a cart by a user in single field of model name orders
is Given Below:
Right now I’m able to save the data but all the items are saved separately instead of being in one field so how I can achieve that and I using cart as session to save it id and size as a key and value
my models.py where I want to save order
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE,)
item = models.ForeignKey(Item, on_delete=models.CASCADE )
status = models.IntegerField(choices = status_choices, default=1)
method = models.CharField(max_length=50, blank=False,)
size = models.CharField(max_length=20, blank=False)
price = models.FloatField(blank=False)
created_at = models.DateField(auto_now=True, editable=False)
payment_status = models.IntegerField(choices = payment_status_choices, default=3)
order_id = models.CharField(unique=True, max_length=200, null=True, blank=True, default=None)
datetime_of_payment = models.DateTimeField(default=timezone.now)
def placeorder(self):
self.save()
def __str__(self):
return self.user.username
my views.py to save the data
and my html where my cart is showing
<tbody style="margin-bottom: 20px;">
{% for item in items %}
<tr>
<th scope="row">{{forloop.counter}}</th>
<td> <img src="{{item.first.url}}" alt="" height="100px"></td>
<td>{{item.name}}</td>
{% if item.swag %}
<td>{{item|cart_size:request.session.cart}}</td>
{% endif %}
{% if not item.swag %}
<td>Regular </td>
{% endif %}
<td>{{item.price|currency}}</td>
<td> <a href="#">Remove</a> </td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</aside>
<aside class="col-lg-3">
<div class="card mb-3">
<div class="card-body">
<form>
<div class="form-group"> <label>Have coupon?</label>
<div class="input-group"> <input type="text" class="form-control coupon" name="" placeholder="Coupon code"> <span class="input-group-append"> <button class="btn btn-primary btn-apply coupon">Apply</button> </span> </div>
</div>
</form>
</div>
</div>
<div class="card">
<div class="card-body">
<dl class="dlist-align">
<dt style="float: left; font-size:20px; margin-right:10px;">Total price:</dt>
<dd style="font-size: 20px; color:#025;" class="text-right">{{items|total_actual_price:request.session.cart|currency}}</dd>
</dl>
<dl class="dlist-align">
<dt style="float: left; font-size:20px; margin-right:10px;">Discount:</dt>
<dd style="font-size: 20px; color:#025;" class="text-right text-danger">{{items|discount_price:request.session.cart|currency}}</dd>
</dl>
<dl class="dlist-align">
<dt style="float: left; font-size:20px; margin-right:10px;">Total Paying Amount:</dt>
<dd style="font-size: 20px; color:#025;" class="text-right"><strong name="price" >{{items|total_price:request.session.cart|currency}}</strong></dd>
</dl>
<hr>
<a href="#" class="btn btn-out btn-primary btn-square btn-main" data-bs-toggle="modal" data-bs-target="#exampleModal"> Make Purchase </a> <a href="#" class="btn btn-out btn-success btn-square btn-main mt-2" data-abc="true">Continue Shopping</a>
</div>
</div>
</aside>
</div>
</div>
As you can see I am using filter to show user total price of his cart so what i want is to save the item id of all the items in items field and size in size field acc to their item and in place of price total price got saved.
But right now it’s saving separate field for item, i don’t want that
my html form for checkout and save orders
<div class="modal-body">
<form action="{% url 'orders:checkout' %}" method="Post">
{% csrf_token %}
<h3>Please Select Your Payment Method</h3> <br>
<div class="method" style="font-size: 23px;">
<input type="radio" value="postpaid" name="payment" style="height: 20px; width: 20px;">
<label for="postpaid">Cash On Delivery😊</label>
<input type="radio" value="Prepaid" name="payment" style="height: 20px; width: 20px;">
<label for="prepaid">Online Payment😎</label>
</div>
</div>
<input type="submit" class="btn float-right btn-primary" value="Go Ahead">
</form>
item model
class Item(models.Model):
categories = models.ForeignKey(Categories, on_delete=models.CASCADE, related_name="our_items")
subcategories = models.ForeignKey(Subcategories, on_delete=models.CASCADE, related_name="products")
name = models.CharField(max_length=200, blank=False)
contain_size = models.CharField(max_length=50, blank=True)
brand_name = models.CharField(max_length=100, blank=False, default="Bagh")
swag = models.BooleanField(blank=False, default=False)
male = models.BooleanField(blank=False, default=False)
female = models.BooleanField(blank=False, default=False)
unisex = models.BooleanField(blank=False, default=False)
first = models.ImageField(upload_to='items', blank=False)
second = models.ImageField(upload_to='items', blank=False)
third = models.ImageField(upload_to='items', blank=True)
fourth = models.ImageField(upload_to='items', blank=True)
fifth = models.ImageField(upload_to='items', blank=True)
sixth = models.ImageField(upload_to='items', blank=True)
seventh = models.ImageField(upload_to='items', blank=True)
rate = models.CharField(max_length=5, choices=rating, default="⭐⭐⭐⭐")
stock = models.CharField(max_length=50, blank=False, default="In Stock")
authentic = models.CharField(max_length=1,blank=False,choices=auth, default="✔")
price = models.FloatField(blank=False,)
actual_price = models.FloatField(blank=False)
type = models.CharField(blank=False, max_length=100, default="Cloth")
joined_date = models.DateTimeField(default=timezone.now,editable=False)
update_at = models.DateTimeField(auto_now=True)
description = models.TextField(blank=True)
@staticmethod
def get_items_by_id(ids):
return Item.objects.filter(id__in = ids)
def __str__(self):
return self.name
and cart view where all the item goes after selected by a user
class Cart(View):
def get (self, request):
cart = request.session.get('cart', None)
if not cart:
cart = {}
request.session['cart'] = cart
ids = (list(cart.keys()))
ids = (list(request.session.get('cart').keys()))
item = Item.get_items_by_id(ids)
address = Address.objects.filter(user=request.user)
print(item)
return render(request, 'cart.html', {'items': item, 'addresses':address })
It does save the item in it but I want to fetch all the items of the car in that field
adding updated model as told
class Order(models.Model):
status_choices = (
(1, 'PENDING'),
(2, 'CONFIRMED'),
(3, 'PACKED'),
(4, 'SHIPPED'),
(5, 'IN WAY'),
(6, 'ARRIVED DESTINATION'),
(7, 'RECIEVED'),
(8, 'COMPLETED')
)
payment_status_choices = (
(1, 'SUCCESS'),
(2, 'FAILURE' ),
(3, 'PENDING'),
)
user = models.ForeignKey(User, on_delete=models.CASCADE,)
address = models.ForeignKey(Address, default= True, on_delete=models.CASCADE )
status = models.IntegerField(choices = status_choices, default=1)
method = models.CharField(max_length=50, blank=False,)
total_price = models.FloatField(blank=False, default=0)
created_at = models.DateField(auto_now=True, editable=False)
payment_status = models.IntegerField(choices = payment_status_choices, default=3)
order_id = models.CharField(unique=True, max_length=200, null=True, default=None)
datetime_of_payment = models.DateTimeField(default=timezone.now)
# related to razorpay
razorpay_order_id = models.CharField(max_length=1000, null=True, blank=True)
razorpay_payment_id = models.CharField(max_length=1000, null=True, blank=True)
razorpay_signature = models.CharField(max_length=1000, null=True, blank=True)
def save(self, *args, **kwargs):
if self.order_id is None and self.datetime_of_payment and self.id:
self.order_id = self.datetime_of_payment.strftime('CODER%Y%m%dODR') + str(self.id)
return super().save(*args, **kwargs)
def __str__(self):
return self.user.username + " " + str(self.order_id) + " " + str(self.created_at)
class OrderItem(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE,)
item = models.ForeignKey(Item, on_delete=models.CASCADE )
size = models.CharField(max_length=20, blank=False)
price = models.FloatField(blank=False)
def __str__(self):
return self.order.order_id
updated views.py
class Checkout(View):
def post (self, request,):
user = request.user
address = Address.objects.filter(default=True, user=request.user)
cart = request.session.get('cart')
items = Item.get_items_by_id(list(cart.keys()))
prefer = request.POST.get('payment')
total_price = request.POST.get('paying_price')
total_price = json.loads(total_price)
with transaction.atomic():
order = Order.objects.create(
user=user,
total_price=total_price,
address=address.first(),
method = prefer,
)
for item in items:
item_order = OrderItem.objects.create(
order=order,
item=item,
size=cart.get(str(item.id)),
price=item.price,
)
request.session['cart'] = {}
return redirect('orders:cart',)
adding the traceback
Environment:
Request Method: POST
Request URL: http://localhost:8000/Check-Out/
Django Version: 3.2.6
Python Version: 3.8.5
Installed Applications:
['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'crispy_forms',
'xhtml2pdf',
'accounts',
'products',
'orders']
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:UsersmithleshDesktopcoolbuylibsite-packagesdjangocorehandlersexception.py", line 47, in inner
response = get_response(request)
File "C:UsersmithleshDesktopcoolbuylibsite-packagesdjangocorehandlersbase.py", line 181, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:UsersmithleshDesktopcoolbuylibsite-packagesdjangoviewsgenericbase.py", line 70, in view
return self.dispatch(request, *args, **kwargs)
File "C:UsersmithleshDesktopcoolbuylibsite-packagesdjangoviewsgenericbase.py", line 98, in dispatch
return handler(request, *args, **kwargs)
File "C:UsersmithleshDesktopcoolbuycoolbuyordersviews.py", line 61, in post
item_order = OrderItem.objects.create(
File "C:UsersmithleshDesktopcoolbuylibsite-packagesdjangodbmodelsmanager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "C:UsersmithleshDesktopcoolbuylibsite-packagesdjangodbmodelsquery.py", line 453, in create
obj.save(force_insert=True, using=self.db)
File "C:UsersmithleshDesktopcoolbuylibsite-packagesdjangodbmodelsbase.py", line 682, in save
self._prepare_related_fields_for_save(operation_name="save")
File "C:UsersmithleshDesktopcoolbuylibsite-packagesdjangodbmodelsbase.py", line 932, in _prepare_related_fields_for_save
raise ValueError(
Exception Type: ValueError at /Check-Out/
Exception Value: save() prohibited to prevent data loss due to unsaved related object 'order'.
Hello I have experience with e-commerce,
as much i have understand that you want to save all the details of item like size , price , name ,mail description in one single field .Is that right ?
if yes , then I think directly it’s not possible you can try one thing —
1.You can take all the desired data and create a dictionary or list out of them
2. Create a single textfield with more maxlength and a FK with user or customer model and then you can save the list or dict as text in that one field in db, and then can retrieve the all data as well as you can get by index also.
But it will be more efficient for storing data only , if you want to retrieve the data efficiently then go for separate models as you have created.
hope it will have, I have applied same type of concept to store customer complain and payment details of users and I like your code , you are following good practice!
adding the answer from @allexiusw
The best practice for this is, you need to create 2 models with many to many relationship for product_order:
class Order(models.Model):
order_group_id = models.AutoField(primary_key=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
product_order = models.ManyToManyField(OrderItem, related_name="order", null=True)
total_price = models.DecimalField(default=0, decimal_places=2, max_digits=10, null=True)
datetime_of_payment = models.DateTimeField(default=timezone.now, null=True)
...
class OrderItem(models.Model):
item = models.ForeignKey(Item, on_delete=models.CASCADE, null=True)
size = models.CharField(max_length=20, blank=False, null=True)
price = models.FloatField(blank=False, null=True)
...
To save it to that 2 models, you can do something like this:
class Checkout(View):
def post (self, request):
user = request.user
address = Address.objects.filter(user=request.user)
cart = request.session.get('cart')
total_price = request.POST.get('price')
items = Item.get_items_by_id(list(cart.keys()))
prefer = request.POST.get('payment')
order = Order.objects.create(
user=user,
date_of_payment=datetime.datetime.now(),
total_price=total_price,
address=address)
for item in items:
prod_order = OrderItem.objects.create(
item=item,
size=item.size,
price=price,
)
order.product_order.add(prod_order)
order.save()
....
You need to split the model Order
in two models:
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE,)
status = models.IntegerField(choices = status_choices, default=1)
method = models.CharField(max_length=50, blank=False,)
payment_status = models.IntegerField(choices = payment_status_choices, default=3)
order_id = models.CharField(unique=True, max_length=200, null=True, blank=True, default=None)
datetime_of_payment = models.DateTimeField(default=timezone.now)
created_at = models.DateField(auto_now=True, editable=False)
def placeorder(self):
self.save()
def __str__(self):
return self.user.username
class ProductOrder(models.Model):
order = models.ForeingKey(Order, on_delete=models.CASCADE)
item = models.ForeignKey(Item, on_delete=models.CASCADE)
size = models.CharField(max_length=20, blank=False)
price = models.FloatField(blank=False)
In that way you will have 1-Order and Many-Productos per Order.
As mentioned in most of the answers, you have to split the models into 2. The Order-OrderItem scenario is a good example for One-to-Many relationship.
Consider you ordering Roti, Paneer (Indian dishes) and Coke from Swiggy (Indian food delivery app). Swiggy says that your order id is #123456.
The models should look like this.
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE,)
status = models.IntegerField(choices = status_choices, default=1)
method = models.CharField(max_length=50, blank=False,)
... other fields ...
The Order model tells that the Order is placed by the user Shreyas, method of payments is X and current status is this.
class OrderItem(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE,)
item = models.ForeignKey(Item, on_delete=models.CASCADE )
size = models.CharField(max_length=20, blank=False)
price = models.FloatField(blank=False)
... any other fields ...
OrderItem model tells that the this item – Coke – is part of Order ID #123456 and the price at the time of ordering is Rs.X.
Your database after saving should look like this –
Order table
id | user | method | status |
---|---|---|---|
123456 | shreyas (id) | payment | order_placed |
OrderItem table
id | order_id | item_id | size | price |
---|---|---|---|---|
1 | 123456 | roti (id) | n | 10.00 |
2 | 123456 | paneer (id) | n | 50.00 |
3 | 123456 | coke (id) | n | 25.00 |
Saving your order
There is a with transaction.atomic
statement in the code, this allows you to write all the models in one go, and revert if any of the write statements fail. Ref: Django transactions
class Checkout(View):
def post (self, request):
user = request.user
address = Address.objects.filter(user=request.user)
cart = request.session.get('cart')
total_price = request.POST.get('price')
items = Item.get_items_by_id(list(cart.keys()))
prefer = request.POST.get('payment')
with transaction.atomic:
order = Order.objects.create(
user=user,
date_of_payment=datetime.datetime.now(),
total_price=total_price,
address=address
)
for item in items:
prod_order = OrderItem.objects.create(
order=order
item=item,
size=item.size,
price=price,
)
... return the view ...