Context `Order.Items` not appearing in PDF file from Django Admin

Viewed 262

Hellooo,

I am trying to add a feature to my admin where I can download order details from a PDF file, which has gone successful so far except that Order.Model is not completely appearing.

so I have 3 models: Item, OrderItem and Order. The Order has a Many-to-Many relationship with OrderItem and the OrderItem has a Foreign Key with Item.

In the template I am trying to loop between the Order.Items which is items = models.ManyToManyField(OrderItem) but it is not rendering any data.

Here is the models.py

class Item(models.Model):
    title = models.CharField(max_length=100)
    price = models.FloatField()

class OrderItem(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                             on_delete=models.CASCADE)
    ordered = models.BooleanField(default=False)
    item = models.ForeignKey(Item, on_delete=models.CASCADE)
    quantity = models.IntegerField(default=1)
    variation = models.ManyToManyField(Variation)

class Order(models.Model):
    items = models.ManyToManyField(OrderItem)

Here is the views.py

@staff_member_required
def admin_order_pdf(request, order_id):
    order = get_object_or_404(Order, id=order_id)
    html = render_to_string('pdf.html', {'order': order})
    response = HttpResponse(content_type='application/pdf')
    response['Content-Disposition'] = 'filename="order_{}.pdf"'.format(Order.id)
    weasyprint.HTML(string=html).write_pdf(response)
    return response

here is the url.py

    path('admin/order/(<order_id>\d+)/pdf/', views.admin_order_pdf, name='admin_order_pdf')

Here is the pdf.html template which is only showing as highlighted

                    Ordered on: {{order.ordered_date}}  <----------Showing
                    
                    {% for order_item in order.items.all %}
                    {{ order_item.item.title }}         <----------Not Showing
                    {% endfor %}

I even tried removing the forloop but still nothing happened

                    Ordered on: {{order.ordered_date}}  <----------Showing
                    
                    {{ order_item.item.title }}         <----------Not Showing
3 Answers

I think I dont have enough information to answer, but from what I see here, you are only passing an order from the view (single order) and not a queryset or any other iterable to the template. Am I missing the queryset or iterable?

If you want to access other objects related to the Order (such as OrderItem) you are missing it in the template. From you models I can see that Order has a relationship with OrderItem and not Items. Items are the one you try to access in the view. (Do you even have an Items model?)

Removing the loop wont work, as there is no order_item variable available in the template.

You are using manytomany field with OrderItem. Hence in order to reach OrderItem, you will have to go via a through model which is created under the hood. You are accessing the through mode when writing order.items.all() instead of your OrderItem model. For further information see django docs here: https://docs.djangoproject.com/en/3.1/topics/db/examples/many_to_many/

Assuming you intention here is to create a manytomany relation between Order and Item models. Then here is how you should create your ManyToMany

class Item(models.Model):
    title = models.CharField(max_length=100)
    price = models.FloatField()

class OrderItem(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, 
                                on_delete=models.CASCADE)
    ordered = models.BooleanField(default=False)
    item = models.ForeignKey(Item, on_delete=models.CASCADE)
    quantity = models.IntegerField(default=1)
    variation = models.ManyToManyField(Variation)

class Order(models.Model):
    items = models.ManyToManyField(Item, through='OrderItem')

This will resolve your problem.

@a-k First of all {{order.ordered_date}} this is not valid. Your Order model doesn't have any field or method ordered_date according to what you've shown here.

Ordered on: {{order.ordered_date}}  # This is wrong and invalid
                    
{% for order_item in order.items.all %}
    {{ order_item.item.title }} # This is valid, use this method to render other fields
{% endfor %}

When you convert your above html code, it will generate pdf like this. item_1 here is the title of item. {{order.ordered_date}} didn't worked instead {{ order_item.item.title }} worked and it was renderd in the pdf.

output.pdf

The second snippet which is shared by you is invalid and will not show any dynamic data. It will just render Ordered on:

Ordered on: {{order.ordered_date}}  <----------Invalid
                
{{ order_item.item.title }}         <----------Invalid

I hope this resolves your queries.

Related