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
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.
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
I have two models that look like;
class Body(models.Model):
body_id = models.AutoField(primary_key=True)
is_adult = models.BooleanField(default=False)
body = models.TextField()
add_user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
add_date = models.DateTimeField()
edit_user = models.CharField(max_length=25, blank=True, null=True)
edit_date = models.DateTimeField(blank=True, null=True)
category = models.ForeignKey(Category, models.CASCADE)
class Meta:
managed = True
db_table = 'jdb_body'
class BodyTag(models.Model):
body_tag_id = models.AutoField(primary_key=True)
body = models.ForeignKey('Body', models.CASCADE)
tag = models.ForeignKey('Tag', models.CASCADE, db_column='tag')
class Meta:
managed = True
db_table = 'jdb_body_tag'
def __str__(self):
return self.tag
I have a view that looks like;
def index(request):
latest_body_list = Body.objects.all().order_by('-body_id')
context = {
'latest_body_list': latest_body_list
}
return render(request, 'index.html', context)
That view gives me a list Body records no problem. I am trying to display Body records with their corresponding BodyTag records. What am I doing wrong?
You neeed a ManyToManyField in your class Body
tags = models.ManyToManyField('Tag')
To access
body = Body.objects.get(body_id=1)
tags = body.tags.all()
EDIT
My previous answer was incorrect because I did not see the more complex relationship. The problem is this: You have many BodyTag objects to one Body object, but you also have many BodyTag objects to one Tag object. So as shown in the image, BodyTag's BT4 and BT5 belong to Body's BODY1 and BODY2. That's why #chipchap31 is talking of a ManyToMany relationship.
If this is not the relationship you want, and from your comments I do not think that you want a ManyToMany field, then the BodyTag model should be either changed, or perhaps discarded. Perhaps relate Body to Tag directly using the ForeignKey, and then have a field in the Tag model that distinguishes it with the type of tag it is, so one type would be body, and you can use a choices field to show all the different types of Tags.
Previous answer (Incorrect)
If you mean displaying these in your template, then all you have to do is follow the ForeignKey relationship backwards. This is shown in the documentaion for the views, but it would look pretty much the same in the template. Something like:
{% for body in latest_body_list %}
{{ body }}
{% for tag in body.tag_set.all %}
{{ tag }}
{% endfor %}
{% endfor %}
The -set is what tells django to look backward in the ForeignKey relationship.
A perhaps better way, also shown in the documentation would be to define a related_name in your ForeignKey:
class BodyTag(models.Model):
body_tag_id = models.AutoField(primary_key=True)
body = models.ForeignKey('Body', models.CASCADE, related_name='tags')
tag = models.ForeignKey('Tag', models.CASCADE, db_column='tag')
Then your template could be written a little better:
{% for body in latest_body_list %}
{{ body }}
{% for tag in body.tags.all %}
{{ tag }}
{% endfor %}
{% endfor %}
I have two models and I want to put an if condition in my template to get the desired output.
I have the first model
class Subject(models.Model):
name = models.CharField(max_length=50)
books = models.ForeignKey('Books')
And the second one
class Books(models.Model):
name = models.CharField(max_length=50)
subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
And a view
def View(request):
subjects = Subject.objects.all()
return render('my_html.html",{'subjects':subjects})
In my model, I have wanted to do this.
{% for subject in subjects %}
{% if subject.books.count > 0 %}
{{ subject.name }}
{% endif %}
{% endfor %}
I thought the only way is to have the models referencing each other for my plan to work but I also think that looping will require more resources. Is there a better way of doing this either thro' the view or just in the models?
You only need to ForeignKey from Book to Subject, so:
class Subject(models.Model):
name = models.CharField(max_length=50)
# no books
class Books(models.Model):
name = models.CharField(max_length=50)
subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
then you can filter the subjects such that it only retrieves Subjects with at least one book:
def view(request):
subjects = Subject.objects.filter(books__isnull=False).distinct()
return render(request, 'my_html.html', {'subjects': subjects})
this will make a LEFT OUTER JOIN on the table of the Book model, and check if there is at least one book. You thus do not need to filter in the template (which is not a good idea anyway).
I have two models like this in my Django project.
class Product(models.Model):
name = models.CharField(max_length=100)
category = models.ForeignKey(ProductCategory, on_delete=models.CASCADE)
sub_category = models.ForeignKey(ProductSubCategory, on_delete=models.CASCADE)
comment = models.TextField()
size = models.CharField(max_length=60)
price = models.FloatField(default=0)
class ProductImage(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
alt = models.CharField(max_length=200)
picture = models.FileField()
Of course a product can have 2 or more pictures.
How can I get a all products with all related every single image in my view and pass the result as a context to the template?
I searched and tried these:
prefetch_related,
select_related,
raw sql query
and couple of suggested ways, but cannot get the result.
You may use prefetch_related to optimise the query
Product.objects.filter(sub_category = sub_category_id).prefetch_related('productimage_set')
Then, in the template
{% for image in product.productimage_set %}
display image here
{% endfor %}
You may alternately set a related_name in the foreign-key, like
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name="product_images")
and then in the view,
Product.objects.filter(sub_category = sub_category_id).prefetch_related('product_images')
end then in the template
{% for image in product.product_images %}
display image here
{% endfor %}
Refer ForeignKey.related_name
I have two models (Item and Album).
Album has a foreign key pointing to Item
like this
class Item(models.Model):
name = models.CharField(max_length=50)
price = models.IntegerField()
and
class Album(models.Model):
photo = ImageField(upload_to=item_directory_path)
caption = models.CharField(max_length=100, blank=True)
item = models.ForeignKey(Item, on_delete=models.CASCADE)
default = models.BooleanField(default=True)
In my template
{% for item in item_tags %}
<div class="item-dock">
<a href="{{item.name}}">
<img src="{{item.album_set.photo.filter(default=True)}}" class="img-responsive">
<p class="item-name text-center">{{item.name|capfirst}}</p>
</a>
Now the real issue is. Given that querying on a related model will always return a queryset. How can I retrieve a single object(in this case, a single photo from this queryset where the "default=True") from the Album model for an Item in my template.
I know about queryset and related manager!
Any help, suggestions will be much appreciated
Just query the first element if that would suffice.
# something like this in your template
{% item.album_set.first.photo %}
You can use a custom template filter for this:
Details about creating and using custom template tags and filters can be found here:
https://docs.djangoproject.com/en/1.11/howto/custom-template-tags/
from django import template
register = template.Library()
def default_image(queryset):
return queryset.get(default=True).photo.url
register.filter('default_image', default_image)
You can use it in the template like this:
{{ item.album_set|default_image }}
Remember you need to {% load your_custom_template_tags %} before using the the filter
I have a django application where I have few manytomany fields and im trying to show these relation records in my listing, however it all comes blank
so this is my model:
class Genre(models.Model):
genre_name = models.CharField(max_length=50)
genre_slug = models.SlugField(max_length=300)
genre_meta = models.TextField(max_length=300)
genre_description = models.TextField(max_length=300)
listing = models.BooleanField(default=True)
def __unicode__(self):
return self.genre_name
class Movie(models.Model):
movie_name = models.CharField(max_length=50)
movie_slug = models.SlugField(max_length=300)
movie_description = models.TextField(null=True, max_length=300)
movie_category = models.ManyToManyField(Category, related_name='category')
movie_genre = models.ManyToManyField(Genre, blank=True, related_name='genre')
listing = models.BooleanField(default=True)
def __unicode__(self):
return self.movie_name
this is my view
def AllMovies (request):
movies= Movie.objects.all().order_by('movie_name')
context = {'movies': movies}
return render_to_response('all.html', context, context_instance=
RequestContext(request))
and this is my template
{% for movie in movies %}
{{ movie }}
{% for genre in movies.genre.all %}{{ genre_name }}{% endfor %}
{% endfor %}
so there are three concerns here:
1- All I get is blank in my template
2- What would be the best option to show items which have been ticked as listed and hide the rest in template
3- This is a data bank so im sure we will need to use load more button in my template, but if i want to show 30 of items instead of all
Try this:
{% for movie in movies %}
{{ movie }}
{% for genre in movie.movie_genre.all %}{{ genre_name }}{% endfor %}
{% endfor %}
You were trying to iterate over movies.genre.all, which is wrong because:
movies is a queryset, you should use movie instead, as it is a Model instance
genre is not a field in the Movie model, you should use movie_genre, which is a ManyToManyField you are looking for
I believe you didn't understand the related_name attribute. It doesn't modify the behavior of the field (in this case: movie_genre). Instead, it changes the name of an attribute in a related model (in this case: Genre). For example, you can use it to get all related movies:
>>> genre = Genre.objects.get(name='Drama')
>>> genre.genre.all()
[<Movie>, <Movie>, ...]
As you can see, your choice of a related_name wasn't so good. Something like movies would be more appropriate.
Please, read the documentation once again, as you probably didn't understand Django's logic around relations (foreign keys, many-to-many, etc.).
Besides, here are a few style-related tips:
don't use movie_ and genre_ prefixes in field names - e.g. it is obvious that genre_name is a name of a genre, since it is defined inside Genre model
try to follow PEP8 (http://legacy.python.org/dev/peps/pep-0008/), as it is a standard in the Python community
read about class-based views (https://docs.djangoproject.com/en/dev/topics/class-based-views/) - you may find them very helpful