Reputation: 1191
I would like to add some Order details(DetailView) in the Order history page(ListView), See image example below ( I have made the image on photoshop). I was able to get the grey part (order list to show) But I am not able to get the item details to show in this page correctly. If I click View Order Detail it goes to detail page where I can show all this. But I need a small summary in the ListPage too. See example below. How do I change my views or templates to achieve this? See my views and templates below
Below is how I need my order history page to look like
Below are my my models.py
class Order(models.Model):
token = models.CharField(max_length=250, blank=True)
total = models.DecimalField(max_digits=6, decimal_places=2, verbose_name='USD Order Total')
emailAddress = models.EmailField(max_length=100, blank=True, verbose_name='Email Address')
created = models.DateTimeField(auto_now_add=True)
billingName = models.CharField(max_length=350, blank=True)
billingAddress1 = models.CharField(max_length=350, blank=True)
billingCity = models.CharField(max_length=100, blank=True)
billingZipcode = models.CharField(max_length=10, blank=True)
billingCountry = models.CharField(max_length=50, blank=True)
class OrderItem(models.Model):
product = models.CharField(max_length=250)
quantity = models.IntegerField()
price = models.DecimalField(max_digits=5, decimal_places=2, verbose_name='USD Price')
order = models.ForeignKey(Order, on_delete=models.CASCADE)
image = models.ImageField()
Below is my template
{% block body %}
<div>
<div class="text-center">
<br/>
<h1 class="text-center my_title">Order Purchase History</h1>
<br/>
{% if order_details %}
{% for order in order_details %}
<div class="row order_detail_div" >
<div class="col-md-2">
<b>Order Number: 1234{{ order.id }}</b><br/>
<small><em>Order Date: {{ order.created|date:"M d Y" }}</em></small>
</div>
<div class="col-md-1"></div>
<div class="col-md-3 text-left">
<b>Status: Paid</b><br/>
<b>Total items in Order: ()</b>
</div>
<div class="col-md-1"></div>
<div class="col-md-3 order_total_text">
<b>Order Total: ${{ order.total }}</b>
</div>
<div class="col-md-2 view_order_details">
<b><a href="{% url 'order:order_detail' order.id %}">View Order Details</a></b>
</div>
</div>
{% for item in order_items %} <!-- This gets the order items from the Order -->
<div class="row order_item_detail_div">
<div class="col-md-2">
<img src="{{ item.image }}" >
</div>
<div class="col-md-2">
{{ item.product}}
</div>
<div class="col-md-2">
{{ item.id}}
</div>
<div class="col-md-2">
{{ item.quantity}}
</div>
<div class="col-md-2">
{{ item.price}}
</div>
</div>
{% endfor %}
<br/>
{% endfor %}
{% else %}
<p>
You do not have any orders yet.<br/><br/>
<a href="{% url 'home' %}" class="btn btn-secondary">Add more recipes</a>
</p>
{% endif %}
</div>
</div>
<br/>
<br/>
{% endblock %}
Views.py 1st try Error: For some reason the print statement in terminal shows correctly but in the browser it shows. See image below code
class OrderHistory(LoginRequiredMixin, ListView):
model = Order
template_name = 'order/order_list.html'
def get_context_data(self, **kwargs):
context = super(OrderHistory, self).get_context_data()
context['order_details'] = Order.objects.filter(emailAddress=self.request.user.email) #The code is correct till here
order_details = Order.objects.filter(emailAddress=self.request.user.email)
for order in order_details:
print("Order items are", OrderItem.objects.filter(order=order))
context['order_items'] = OrderItem.objects.filter(order=order)
return context
The Order details in grey are correct but the OrderItems do not correspond to the order. It should be like in the 1st image(above)
Views.py 2nd try Error: I thought will do OrderItem.objects.all())
in the views.py and the forloop in the template will fix the view but below is the error I get
class OrderHistory(LoginRequiredMixin, ListView):
model = Order
template_name = 'order/order_list.html'
def get_context_data(self, **kwargs):
context = super(OrderHistory, self).get_context_data()
context['order_details'] = Order.objects.filter(emailAddress=self.request.user.email) #The code is correct till here
print("Order items are", OrderItem.objects.all())
context['order_items'] = OrderItem.objects.all()
return context
Again this is not correct these 2 items do not belong to Order. See the 1st image for how it should be. The grey part which contains order details is still correct
Upvotes: 0
Views: 2032
Reputation: 151
My way was with a method inside Order returning a Queryset
models.py
class Order(models.Model):
...
def items(self):
return OrderItem.objects.filter(order=self)
Call items inside a loop
Orderlist.html
{% for order in order_details %}
{% for item in order.items %}
<!-- YOUR HTML STUFFS -->
{% endfor %}
{% endfor %}
Upvotes: 0
Reputation: 2223
Its a bit confusing, but i guess i manage to get the points...
The problem is here... since Django is not dinamic, they will send your data after processing everything to your template... what you doing here is overring every value at your context['order_items']
so they will send to template only the last data.
for order in order_details:
context['order_items'] = OrderItem.objects.filter(order=order)
When you made the change to context['order_items'] = OrderItem.objects.all()
without the forloop now you getting all Order Item without set any order... so when you try to show the OrderItem from first Order they will show all OrderItem.
The Sollution is use TEMPLATETAGS so that way you can filter your data while the HTML is processing...
So you need to filter your OrderItem based in your Order... since in your page you will show many orders and you must filter the OrderItem based in this Orders
create one folder called templatetags inside your app, create one file to be your templatetag (dont forget to create __init__.py
file too) in that case we will call order_templatetags.py
app/templatetags/order_templatetags.py
from django import template
from .models import OrderItem
register = template.Library()
@register.filter
def filter_order_items(self):
return OrderItem.objects.filter(order__id=self.id)
IN YOUR HTML
<!-- IN TOP OF YOUR HTML (After Extends if you have it) PLACE THIS CODE -->
{% load order_templatetags %}
<!-- REST OF YOUR HTML CODE -->
{% for order in order_details %}
<!-- REST OF YOUR HTML CODE -->
{% for item in order|filter_order_items %} <!-- This gets the order items from the Order -->
<!-- YOUR HTML STUFFS -->
{% endfor %}
{% endfor %}
My Snippet: https://github.com/Diegow3b/django-utils-snippet/blob/master/template_tag.MD
Django Docs: https://docs.djangoproject.com/en/2.0/howto/custom-template-tags/
Upvotes: 1
Reputation: 1191
Well I did finally figure it out
models.py (Add related_name in order field)
class OrderItem(models.Model):
product = models.CharField(max_length=250)
quantity = models.IntegerField()
price = models.DecimalField(max_digits=5, decimal_places=2, verbose_name='USD Price')
order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name="order_cushions")
image = models.ImageField()
views.py is
class OrderHistory(LoginRequiredMixin, ListView):
model = Order
template_name = 'order/order_list.html'
def get_context_data(self, **kwargs):
context = super(OrderHistory, self).get_context_data()
context['order_details'] = Order.objects.filter(emailAddress=self.request.user.email)
print("Order items are", OrderItem.objects.all())
context['order_items'] = OrderItem.objects.all()
return context
and finally in templates.py
{% for order in order_details %}
{% for item in order.order_cushions.all %} #this is the models related_name
{{item.image}} #this will get your image
{{item.product}}
{{item.price}}
{{item.quantity}} #decorate using bootstrap the way you like
{% endfor %}
{% endfor %}
Upvotes: 0