Samir Tendulkar
Samir Tendulkar

Reputation: 1191

Showing listview and detailview in the same template using forloops in Django

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

enter image description here

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) enter image description here

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

enter image description here

Upvotes: 0

Views: 2032

Answers (3)

ineedme
ineedme

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

Diego Vin&#237;cius
Diego Vin&#237;cius

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

Samir Tendulkar
Samir Tendulkar

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

Related Questions