Search code examples
pythondjangotemplatesviewm2m

How to show the values of intermediate m2m model in Django template


I am iterating through a queryset in the template using template tags to show the data of existing database entries (i.e. Product Details of each product in a customer order). However, I want to show users some values (i.e. quantity and price) that are located in an intermediate m2m model between the Product and Order models for each product in the order.

My approach was to create the querysets in views and pass them through context into the template but I cannot seem to 'call' the values at the template using template tags for the intermediate m2m data. Perhaps my context is passing the wrong queryset or my approach is just wrong.

My code is below and I am thankful for any help you can give:

Models.py Snippet

class Product(models.Model):
    article_number = models.ForeignKey('Article_number')
    color = models.ForeignKey('Color')
    size = models.ForeignKey('Size')
    barcode = models.ForeignKey('Barcode')
    product_name = models.ForeignKey('Product_name')
    brand = models.ForeignKey('Brand')
    category = models.ForeignKey('Category')
    manufacturer = models.ForeignKey('Manufacturer')
    cost_currency = models.ForeignKey('Cost_Currency', null=True, blank=True)
    cost_price = models.DecimalField(decimal_places=2, max_digits=10)
    selling_currency = models.ForeignKey('Selling_Currency', null=True, blank=True)
    selling_price = models.DecimalField(decimal_places=2, max_digits=10)
    description = models.TextField(null=True, blank=True)
    created_on = models.DateTimeField(auto_now_add=True, auto_now=False)
    updated_on = models.DateTimeField(auto_now_add=False, auto_now=True)
    last_edited_by = models.ForeignKey(User, null=True, blank=True)
    active = models.BooleanField(default=True)

    class Meta:
        unique_together = (("article_number", "size", "color"))
        verbose_name = "Product"
        verbose_name_plural = "*Products*"

    def __unicode__(self):
        return str(self.article_number) + "-" + str(self.color) + "-" + str(self.size)


class Order(models.Model):
    order_status = models.ForeignKey('OrderStatus')
    products = models.ManyToManyField(Product, through='OrderProductDetails', through_fields=('order','product'), null=True, blank=True)
    counter = models.ForeignKey(Counter, null=True, blank=True)
    order_type = models.ForeignKey('OrderType')
    order_remarks = models.CharField(max_length=1000, null=True, blank=True)
    order_date = models.DateTimeField(auto_now_add=True, auto_now=False)
    ordered_by = models.ForeignKey(User, null=True, blank=True)
    promo = models.ForeignKey('promo.Promo', verbose_name="Order for which Promotion (if applicable)", null=True, blank=True)
    delivery_date = models.DateField(blank=True, null=True)
    delivery_remarks = models.CharField(max_length=1000, null=True, blank=True)
    updated_on = models.DateTimeField(auto_now_add=False, auto_now=True)

    class Meta:
        verbose_name = "Order"
        verbose_name_plural = "*Orders*"

    def __unicode__(self):
        return str(self.id)


class OrderProductDetails(models.Model):
    order = models.ForeignKey('Order')
    product = models.ForeignKey('products.Product')
    quantity = models.PositiveIntegerField()
    selling_price = models.DecimalField(decimal_places=2, max_digits=10)
    order_product_remarks = models.ForeignKey('OrderProductRemarks',blank=True, null=True)

    class Meta:
        verbose_name_plural = "Order - Product Details"
        verbose_name = "Order - Product Details"

    def __unicode__(self):
        return str(self.id)

Views.py Snippet

from django.shortcuts import render, Http404, HttpResponseRedirect
from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib import messages
from django.forms.models import modelformset_factory
from orders.models import Order, OrderStatus, OrderProductDetails

@login_required(login_url='/admin/login/?next=/')
def warehouseOrder(request, id):
    try:
        order = Order.objects.get(id=id)
        products = order.products.all()

        orderDetails = OrderProductDetails.objects.filter(order__id=id)

        context = {'order': order, 'products': products, 'orderDetails': orderDetails}
        template = 'warehouse_order.html'
        return render(request, template, context)
    except:
        raise Http404

Template.html Snippet

        <table class="table table-striped table-bordered">
            <tr>
                <th class="bottom-align-th">#</th>
                <th class="bottom-align-th">Article No.</th>
                <th class="bottom-align-th">Color</th>
                <th class="bottom-align-th">Size</th>
                <th class="bottom-align-th">Quantity</th>
                <th class="bottom-align-th">Unit Price</th>
                <th class="bottom-align-th">Remarks</th>
                <th class="bottom-align-th">Barcode No.</th>
                <th class="bottom-align-th">Packed</th>
            </tr>
           {% for product in products %}
            <tr>
                <td>{{ forloop.counter }}</td>
                <td>{{ product.article_number }}</td>
                <td>{{ product.color }}</td>
                <td>{{ product.size }}</td>
                <td>{{ orderDetails.quantity }}</td>
                <td>{{ orderDetails.selling_price }}</td>
                <td>{{ orderDetails.order_product_remarks }}</td>
                <td>{{ product.barcode }}</td>
                <td><input type="checkbox" id="large-checkbox"></td>
            </tr>
            {% endfor %}
        </table>

Btw, using Django 1.7.2 here.


Solution

  • You're looping over the wrong thing. Each Order has multiple OrderProductDetails related objects, each of which has exactly one Product. But you're looping over all products. Try something like:

    {% for detail in orderDetails %}
        <tr>
            <td>{{ forloop.counter }}</td>
            <td>{{ detail.product.article_number }}</td>
            <td>{{ detail.product.color }}</td>
            <td>{{ detail.product.size }}</td>
            <td>{{ detail.quantity }}</td>
            <td>{{ detail.selling_price }}</td>
            <td>{{ detail.order_product_remarks }}</td>
            <td>{{ detail.product.barcode }}</td>
            <td><input type="checkbox" id="large-checkbox"></td>
        </tr>
    {% endfor %}