Django - Models FK/many2many relationships - django

I need to understand a bit better how do FK/m2m relationships work.
I've prepared Images model for uploading images and it has an additional feature - it can be categorized by adding to a specific gallery (m2m relation to gallery).
To access gallery name I just had to do a query set for example:
Images.objects.filter(gallery__gallery_name = '')
I'd like to reverse the query a little bit so from Gallery model I can access pictures which are in specific gallery (gallery_name).
How I can do that?
Models:
class Images(models.Model):
image = models.ImageField(upload_to=update_filename, blank=True, null=True, verbose_name="Obrazek")
gallery = models.ForeignKey('Gallery', blank=True, null=True)
class Gallery(models.Model):
gallery_name = models.CharField(max_length=128)
gallery_description = models.TextField(blank=True, null=True)
View:
def index(request):
p = Gallery.objects.filter(gallery_name="main").order_by('-id')
return TemplateResponse(request, 'gallery.html',
{'gallery': p,
},)
Template:
{% for n in gallery.all %}
<h2 class="center">{{n.gallery_name}}</h2>
<hr>
{% for n in gallery.images_set %}
<div class="grid_4">
{{ n.image }}
</div>
{% endfor%}

Try something along the lines of:
# models.py
class Gallery(models.Model):
name = models.CharField(max_length=30)
description = models.CharField(max_length=200, null=True)
images = models.ManyToMany(Image)
class Image(models.Model):
title = models.CharField(max_length=30)
caption = models.CharField(max_length=200, null=True)
image = models.ImageField(upload_to=SOMEPLACE_FOR_MEDIA)
From here you should be able to do things like:
image = Image.objects.get(title="Girl Holding Cheese")
related_galleries = image.gallery_set.all()
or something similar as needed to pull what you want. The same goes the other way. To pull all images in a gallery you would do
gallery = Gallery.objects.get(name="Cheesy Wimmin")
related_images = gallery.images.all()
Though the assignments at the end aren't necessary, I usually just pass gallery.images.all() or image.gallery_set.all() directly. Note the "_set" at the end of the reference from the object that does not contain the M2M definition.
On the subject of direct usage, you can do compound references like
Image.objects.get(title="Girl Holding Cheese").gallery_set.all()
as well, but you have to decide when this makes code more clear and concise and when it just makes it more confusing to read later.
I hope this put you in the right direction.
Update
In your comment below you noticed that you cannot do
images = Images.objects.filter(gallery_set="Cheesy Wimmins")
related_galleries = images.gallery_set.all()
This is because you would be trying to filter() or all() on a queryset, not an individual model. So to make this work you can use a for loop in your template. Something like
# views.py
galleries = Gallery.objects.all()
return render(request, 'some/template.html', {'galleries': galleries})
And then
<!-- templates/some/template.thml -->
{% for gallery in galleries %}
<div class="gallery">
<h2>{{ gallery.name }}</h2>
{% for item in gallery.images.all %}
<div class="image">
{{ item.image }}
</div>
{% endfor %}
</div>
{% endfor %}
or something like this. Of course, you need to do whatever formatting steps you want to make this look right, but that's a way to get at your data.

The problem is with the {% for n in gallery.images_set %} bit in your template. images_set is a related manager, not a queryset. To get a queryset, you need to call all or another of the DBAPI methods that return querysets. So, just change it to gallery.images_set.all, and you're good.

gallery is a QuerySet - it doesn't have a images_set.
This is where naming your variables more appropriately can easily start preventing these problems: for example, galleries would be more appropriate for a list of Gallery objects... then, galleries.images_set would immediately raise red flags.
Anyways, you need to call images_set on what you've called n
{% for n in gallery.all %}
<h2 class="center">{{n.gallery_name}}</h2>
<hr>
{% for n in n.images_set.all %}
<div class="grid_4">
{{ n.image }}
</div>
{% endfor%}

Related

django: Exception has occurred: IndexError pop from empty list

I have been building a website where each essay may have a video object. For some reason, this error in Django continues to persist and I cannot understand where it comes from and how to fix it.
The error related to views.py
Exception has occurred: IndexError
pop from empty list
File "C:\foo\project\bar\views.py", line 65, in index_dev
return render(
IndexError: pop from empty list
views.py:
def index_dev(request):
essay = EssayCls.objects.all().order_by('-date')[:3]
video_obj = VideoObject.objects.all()
user_agent = get_user_agent(request)
context = {
'text_content': essay,
'show_text_content': True,
'video_obj': video_obj
}
if user_agent.is_pc:
return render(
request,
'dev/index_page.html',
context
)
models.py:
...
from embed_video.fields import EmbedVideoField
...
class VideoObject(models.Model):
video_item_url = EmbedVideoField() # same like models.URLField()
added_date = models.DateTimeField(auto_now_add=True, null=True)
title_video = models.CharField(max_length=200)
class Meta:
ordering = ['-added_date']
class EssayCls(models.Model):
title = models.CharField(max_length=200)
organizer_email = models.EmailField(null=True)
date = models.DateField(null=True)
slug = models.SlugField(unique=True, db_index=True)
description = models.TextField(validators = [MinLengthValidator(10)])
details = models.TextField()
image = models.ImageField(upload_to='images')
video = models.ForeignKey(VideoObject, blank=True, null=True, on_delete=models.SET_NULL, related_name="video_obj")
# video = models.ManyToManyField(VideoObject, blank=True, related_name="video_obj") # Many-to-many relationship
language = models.ForeignKey(ProgLang, null=True, on_delete=models.SET_NULL)
guest = models.ManyToManyField(SendMeMessage, blank=True)
tags = models.ManyToManyField(Tag)
template.html:
{% for essay_item in text_content %}
<div class="contact-details">
<h3>{{ title}}</h3>
<p>{{ essay_item.description|convert_markdown|safe }}</p>
<br /><br />
<p>Does this video work? check below:</p>
{% comment %} {% for vid in video_obj %}
<p>{% video vid.video_item_url %}</p>
{% endfor %} {% endcomment %}
{% if essay_item.video %}
{% video %}
{{ essay_item.video.video_item_url }}
{% endvideo%}
{% endif %}
</div>
{% endfor %}
I'm able to display all videos from DB everywhere (i.e. all videos from DB in every essay) with:
{% for vid in video_obj %}
<p>{% video vid.video_item_url %}</p>
{% endfor %}
But, even with {% if video.video_item_url %} I'm unable to display a particular video/s that is/are linked with one-to-many relationship to my EssayCls class. You can see my attempt in template.html (at the end):
{% if essay_item.video %}
{% video %}
{{ essay_item.video.video_item_url }}
{% endvideo%}
{% endif %}
I thought that the error is related to the fact that not all essays have a video i.e. that the key for an essay that doesn't have a video is none/empty. So I did this Boolean comparison hoping it will fix it, but it doesn't. The error is the same with and without this comparison.
UPDATE:
At this point, I can access a particular essay(dev/article-content_pc_tablet.html) with connected videos by:
class MyEssaysView(View):
def get(self, request, slug):
user_agent = get_user_agent(request)
selected_essay = EssayCls.objects.get(slug=slug)
user_feedback = UserFeedback()
context = {
'essay_found': True,
'essay_all': selected_essay,
'post_tags': selected_essay.tags.all(),
'form': user_feedback,
}
if request.method == 'GET':
if user_agent.is_pc:
return render(
request,
'dev/article-content_pc_tablet.html',
context
)
article-content_pc_tablet.html:
<p>{% video essay_all.video.video_item_url %}</p>
As I said, this part works.
The difference between those two views and pages (the one with an error and another one that works) is the fact that in my class for a single essay MyEssaysView in views.py I have selected_essay = EssayCls.objects.get(slug=slug). On the other hand in index_dev function, I have essay = EssayCls.objects.all().order_by('-date')[:3]. It is so because on my index_dev page I have several essays displayed from model EssayCls. I don't know how can I display adequate esay list on my "index page" with adequate video that is related to the particular essay from that list. All I understand from that error is the fact that the process tries to find those videos but has the wrong logic it hit an empty list.
Try to do something like this?
{% if essay_item.video %}
{% if essay_item.video.video_item_url %}
{% video %}
{{ essay_item.video.video_item_url }}
{% endvideo%}
{% else %}
<p>No video</p>
{% endif %}
{% else %}
<p>No video</p>
{% endif %}
I found the solution for this bug. Inspired by this and my own solution for a similar issue I had in another template. There are a few bugs in the template's for loop.
First of all, I should choose text_content from context instead of video_obj. With that, I am able to display a particular video for a particular essay. Next, having a relation one-to-many I can simply loop that in the template using the proper syntax:
{% for vid in text_content %}
<p>{% video vid.video.video_item_url %}</p>
{% endfor %}
But, in the meantime, I realized that it's a good idea to have the possibility to have several videos that can be added to several essays (*I added this part to the class EssayCls in my question: # video = models.ManyToManyField(VideoObject, blank=True, related_name="video_obj") # Many-to-many relationship). Having that in mind I needed to set up my main page in such a way that for each essay's div I needed to be able to display all videos from many-to-many relations.
To do that the proper syntax should include the chained part .all because the essay can have a query set of multiple videos that have to be handled. So I need to loop over text_content from EssayCls (which has a video column which is in many-to-many relation with VideoObject), after that I need to chain the iterator with the video column, then with all (because there may be more than one video) then with a link to a particular video from VideoObject class i.e. video_item_url be:
{% for vid in text_content %}
<p>{% video vid.video.all.video_item_url %}</p>
{% endfor %}
Of course in this case the column video = models.ForeignKey(VideoObject, blank=True, null=True, on_delete=models.SET_NULL, related_name="video_obj") in EssayCls class should be hashed or deleted.

Django - usage of prefetch_related

no matter how many tutorials/documentation i read i still don't quite understand how exactly i'm supposed to be using prefetch_related.
My models.py:
class ProfileComment(models.Model):
author = models.ForeignKey('Profile', on_delete=models.CASCADE, null=True)
date_posted = models.DateTimeField(default=timezone.now, editable=False)
body = models.CharField(max_length=180, blank=True)
...
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
comments = models.ManyToManyField(ProfileComment, related_name='comments', blank=True)
avatar = models.FileField(upload_to=avatar_folder, default='user-avatar/default.png')
...
My views.py:
profile = Profile.objects.prefetch_related('comments').get(user=request.user)
And in template:
{% for comment in profile.comments.all %}
<div>
<p>Author: {{ comment.author.user }}</p><img src="{{ comment.author.avatar.url }}">
<p>Message: {{ comment.body }}</p>
<p>Date posted: {{ comment.date_posted }}</p>
</div>
{% endfor %}
However, no matter what i put in prefetch_related, amount of queries just go up by like 5 for every record
.prefetch_related(…) [Django-doc] should be used to fetch related objects for a queryset in bulk. Since you write:
….get(user=request.user)
however it will not make any difference, since you only retrieve a single object, and it will thus prefetch the comments in an extra query.
What you can minimize however is the number of extra queries that originate from comment.author.user. This will make two extra queries per comment, since ForeignKeys are loaded lazily.
Your view can thus look like:
profile = Profile.objects.get(user=request.user)
comments = profile.comments.select_related('author', 'author__user')
where you pass both profile and comments to the template, and then retrieve it with:
{% for comment in comments %}
<div>
<p>Author: {{ comment.author.user }}</p><img src="{{ comment.author.avatar.url }}">
<p>Message: {{ comment.body }}</p>
<p>Date posted: {{ comment.date_posted }}</p>
</div>
{% endfor %}
here we thus read the comments from a queryset where we fetch the profile and the user of that profile in the same query. As a result we will make two queries: one for the Profile, and one for all the Comments including the .author and .author.user.

Models inherit fields from other models in Django

I have the following models. I am trying to get the newlistitem model to inherit the same image from the above, if that makes sense. I see that I passed through user as a parameter when calling listitem.user and it works fine, but can't seem to grab the picture of the related object.
HTML Render
I am returning both objects to the form and call
{% for item in listitems %}
<div id = "indivlistitem">
<b>{{item.list_name|title}}</b>
<li><img src="/media/{{ item.list_picture }}"/></li>
<li>{{item|title}}</li>
</div>
{% endfor %}
#MODELS
from django.db import models
from django.contrib.auth.models import User
class newlist(models.Model):
user = models.ForeignKey(User)
list_name = models.CharField(max_length = 100)
picture = models.ImageField(upload_to='profiles/')
def __str__(self):
return self.list_name
class newlistitem(models.Model):
user = models.ForeignKey(User)
list_name = models.ForeignKey(newlist)
list_item = models.CharField(max_length = 200)
list_picture = models.ImageField(newlist.picture)
def __str__(self):
return self.list_item
First things first, list_picture = models.ImageField(newlist.picture)
is not going to work. However, it did provide some insight into what you're trying to do.
Since you already have a foreign key to a list in the newlistitem model (your list_name field), you can access the picture that it's linked to by traversing the foreign key, as such.
You'll note that I've also used the url property that all ImageFields contain, to automatically populate the URL of the picture:
{% for item in listitems %}
<div id = "indivlistitem">
<b>{{item.list_name|title}}</b>
<li><img src="{{ item.list_name.picture.url }}"/></li>
<li>{{item|title}}</li>
</div>
{% endfor %}
UPDATE
Some of the pictures that you are trying to access are blank, so you will need to validate that there is an image associated with each entry.
{% for item in listitems %}
<div id = "indivlistitem">
<b>{{item.list_name|title}}</b>
{% if item.list_name.picture %}
<li><img src="{{ item.list_name.picture.url }}"/></li>
{% endif %}
<li>{{item|title}}</li>
</div>
{% endfor %}

Django formset access initial data in template

I have a Django formset with a set of initial data which loads a foreignkey relation object into the initial form:
{{ cellcountformset.management_form }}
{% for form in cellcountformset %}
<div id="">
{{ form.errors }}
{{ form.as_p }}
</div>
{% endfor %}
The relevant models look like this:
class CellType(models.Model):
readable_name = models.CharField(max_length=50)
machine_name = models.CharField(max_length=50)
comment = models.TextField(blank=True)
class CellCount(models.Model):
cell_count_instance = models.ForeignKey(CellCountInstance)
cell = models.ForeignKey(CellType)
normal_count = models.IntegerField()
abnormal_count = models.IntegerField()
comment = models.TextField(blank=True)
I want to be able to display the machine_name of the cell referred to by the cell attribute of the CellCount model as the #id of the div. I use a ModelFormSet for the CellCount, which is passed a list of CellType objects as its initial data.
The form's initial data is stored in form.initial, so try:
{{ form.initial.cell.machine_name }}
I don't think that you can use the form fields to traverse models and get at the machine name, but I'll check. If you can, the syntax will be something like
<div id="{{ form.fields.cell.machine_name }}">
But I think that you are going to need to write a custom template filter for this. It can just take the form as an argument, and return the machine name associated with it (or blank if the form is unbound). Then you will be able to write
<div id="{{ form|machine_name }}">

Django: How to get data connected by ForeignKey via Template?

Since a few weeks I am learning Python and Django. Up to this point it has been enough to read the questions and the answers of other users.But now the moment of my first own question has come.
I will try to describe my problem as best i can. My problem is that I cant query or get the data I want.
I want to get the url of the first object of class Image which is associated by ForeignKey to a Gallery, which is associated by ForeignKey to the class Entry.
Here the models.py so far:
class BlogEntry(models.Model):
...
title = models.CharField(max_length=100)
...
class Gallery(models.Model):
entry = models.ForeignKey('BlogEntry')
class Image(models.Model):
gallery = models.ForeignKey('Gallery')
picture = models.ImageField(upload_to='img')
The View:
def view(request):
return render_to_response('mainview.html', {
'entryquery': BlogEntry.objects.all(),
}
)
The Template:
{% for item in entryquery %}
<h1>{{ item.title }}</h1>
<img src="{{ item.WHAT TO ENTER HERE? :) }}" />
{% endfor %}
It is clear what I want?
Could somebody help me and when possible write a short explanation?
greetings
Bastian
You can access related members just like other attributes in a template, so you can do something like: item.gallery_set.all.0.image_set.all.0.picture.img. However, it might be easier to define a method on BlogEntry that looked up and returned the appropriate picture, so that you could just do item.first_image or something like that
class BlogEntry(models.Model):
...
title = models.CharField(max_length=100)
...
class Gallery(models.Model):
entry = models.ForeignKey('BlogEntry',related_name="galleries")
class Image(models.Model):
gallery = models.ForeignKey('Gallery',related_name='images')
picture = models.ImageField(upload_to='img')
You have to add related_name in foreign key in gallery model and in template view:
{% for g in blogentry.galleries.all %}
{{g.name}}
{%for i in g.images.all %}
<img src="{{i.picture.url}}">{{i.picture}}</img>
{% endfor %}
{% endfor %}