Django - usage of prefetch_related - django

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.

Related

Django query from multiple table base on FK

hi i am new in django coding
i have 3 tables(models) in my different Django app , i try to make a simple report in htmpl page , so need to retrive specific data for each item base on foreign key.
by below code i can accress machine stored table in Html page by
making a loop , but i want to fetch data from other table and filter them ,
{% for m in machines %}
<h2>{{m.title}}</h2>
<h3>{{}}</h3>.
//i** need to access the ordertitle field in tb_order base on machine id. ??**
<p>{{ }}</p>.
// **access ordertitle in tb_order base on status field ??**
{%end for %}`
here is a view.py
def allmachinesLOGO(request):
machines=Machine.objects.all()
context ={'machines':machines}
return render(request,'pline/machineslogos.html',context)
Models :
class tb_order(models.Model):
ordertitle= models.CharField(max_length=200)
customer=models.ForeignKey(Customer, null=True, on_delete=models.SET_NULL)
mould=models.ForeignKey(Mould, null=True, on_delete=models.SET_NULL, blank=True)
status=models.CharField(choices=status_choices, default="0", max_length=20)
accept=models.BooleanField
machine=models.ForeignKey(Machine,null=True, on_delete=models.SET_NULL, blank=True)
id =models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False, unique=True)`
class Machine(models.Model):
title = models.CharField(max_length=200)
machine_model = models.CharField(max_length=200, null=True, blank=True)
description = models.CharField(max_length=600, null=True, blank=True)
featured_image=models.ImageField(default="defaultmachine.webp", blank=True, null=True)
id=models.UUIDField(default=uuid.uuid4, unique=True,primary_key=True,editable=False)`
advice a solution
access ordertitle in tb_order base on status field ??
// i need to access the ordertitle field in tb_order base on machine id.
By using the primary model, you can get the data associated with it from the secondary model. To do this, a special property (object) with the name secondary model_set is created in the primary model by default. In your case, for example: tb_order_set(lower case). Using related_name for the ForeignKey (in which case it will be whatever you write it without the _set prefix).
In the template, the outer loop loops through the rows of the model: Machine. The internal one, through tb_order_set.all, accesses the rows of the tb_order model. Although in my opinion it is better to do all calculations and comparisons in views, and in templates only display information.
Template file:
{% for m in machines %}
<p>{{ m.tb_order_set.all }}</p>
{% for r in m.tb_order_set.all %}
<p>{{ 'ordertitle' }} : {{ r.ordertitle }} {{ 'status' }} : {{ r.status }}</p>
{% endfor %}
{% endfor %}
To show by specific status:
{% for m in machines %}
<p>{{ m.tb_order_set.all }}</p>
{% for r in m.tb_order_set.all %}
{% if r.status == '2' %}
<p>{{ 'ordertitle' }} : {{ r.ordertitle }} {{ 'status' }} : {{ r.status }}</p>
{% endif %}
{% endfor %}
{% endfor %}

how reverse query ManytoMany Django

I have these two models and I want to display the project with a specific task in my page. These are my models:
class Project (models.Model):
name = models.CharField(verbose_name="Project name", max_length=25)
tasks = models.ManyToManyField(Task, verbose_name="tasksInProject", blank=True,
related_name="projects+")
class Task(models.Model):
name = models.CharField(verbose_name="Task", max_length=50)
I call this view:
class TaskToProjectFilterView(DetailView):
model = Task
template_name = "vivs/task_filter.html"
context_object_name = "task_filter"
And this is my html template:
<h4>filter : {{ task_filter }} </h4>
<h4>projects :
{% for element in task_filter.projects.all %}
{{ element }}
{% endfor %}
</h4>
This code displays the {{ task_filter }} but not the list of {{ task_filter.projects.all }}.
Can you help me? I don't understand my mistake. Thanks!
As schwobaseggl stated, remove the + sign and it should work as expected, using:
{% for element in task_filter.projects.all %}
From the Django Docs:
If you’d prefer Django not to create a backwards relation, set related_name to '+' or end it with '+'. For example, this will ensure that the User model won’t have a backwards relation to this model:

Django: Difficulty understanding how to access field values in templates

The following code is giving me an output I don't expect. Help to interpret my code would be appreciated (I'm very new to Django).
I've created this model:
class Question(models.Model):
author = models.ForeignKey('auth.User')
question = models.TextField()
created_date = models.DateTimeField(
default=timezone.now)
def __str__(self):
return self.question
With a corresponding view:
def home(request):
questions = Question.objects.filter(created_date__lte=timezone.now()).order_by('created_date')
return render(request, 'core/home.html', {'questions':questions})
That is called by an HTML template:
<div id = "centreBlock" >
{{ questions }}
{{questions.created_date}}
</div>
<div id = "rightBlock">
<h2> Other questions</h2>
{% for quest in questions %}
<h3>{{ quest.created_date }}</h3>
<p>{{ quest.text|linebreaks }}</p>
{% endfor %}
</div>
The second line of Django code {{ questions.created_date}} in the template doesn't give anything (all the other code works as I expect). Why is this? I was expecting to see a list of the Created Dates.
Because questions is a QuerySet and has no attribute created_date.
If you want to display all dates from a QuerySet then you'd have to get it explicitly or loop over and show each questions created_date
{% for question in questions %}
{{ question.created_date }}
{% endfor %}
The template variable questions is a queryset of questions. You can't access individual fields from the queryset, you have to loop through it.
{% for question in questions %}
{{ question.created_date }}
{% endfor %}

Django - Models FK/many2many relationships

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%}

get request.session from a model method in django

Hay, is it possible to a get a request.session value from a model method in django?
MEGA UPDATE
Here is my model
class GameDiscussion(models.Model):
game = models.ForeignKey(Game)
message = models.TextField()
reply_to = models.ForeignKey('self', related_name='replies', null=True, blank=True)
created_on = models.DateTimeField(blank=True, auto_now_add=True)
userUpVotes = models.ManyToManyField(User, blank=True, related_name='threadUpVotes')
userDownVotes = models.ManyToManyField(User, blank=True, related_name='threadDownVotes')
votes = models.IntegerField()
def html(self):
DiscussionTemplate = loader.get_template("inclusions/discussionTemplate")
return DiscussionTemplate.render(Context({
'discussion': self,
'replies': [reply.html() for reply in self.replies.all().order_by('-votes')]
}))
def _find_users_who_have_voted(self):
user_list = []
for user in self.userDownVotes.all():
user_list.append(user.id)
for user in self.userUpVotes.all():
user_list.append(user.id)
return user_list
users_voted = property(_find_users_who_have_voted)
and my view is called like this
<ul>
{% for discussion in discussions %}
{{ discussion.html }}
{% endfor %}
</ul>
and the template
<li>
<small>
({{ discussion.votes }} votes)
{% if user_id not in discussion.users_voted %}
user not in list!
{% endif %}
</small>
<strong>{{ discussion.message }}</strong>
{% if replies %}
<ul>
{% for reply in replies %}
{{ reply }}
{% endfor %}
</ul>
{% endif %}
the value 'user_voted' returns a list of user ids who has voted on this discussion.
I want to see if the request.session['user'].id value is inside this list
Why don't you use Django's render_to_string inside a view, which would have request happily available to it, and avoid model method altogether?
UPDATE: after skimming your mega update, you should look into Django's inclusion tags and use the data from the model to fill a template, not use a model to render the template. Keep your model and your template separate - Django is an MVT / MCV framework for good reason :)
you can access the current user, and so their session using threadlocals middleware
http://code.arcs.org.au/gitorious/django/django-andsome/blobs/ee8447e3dad2da9383ff701ec640b44cd50d2b0a/middleware/threadlocals.py
but keep in mind:
http://code.djangoproject.com/wiki/CookBookThreadlocalsAndUser
There might be better solutions to your problem. Maybe you'd like to elaborate why you need request.session on model level?
UPDATE:
since you explicitly call some method - probably from a view - why don't you just put the request.user as parameter to your html method?
models.py:
def html(self, user):
your code...
views.py:
yourmodel.html(request.user)
UPDATE to your MEGA UPDATE:
This is exactly what {% include %} is for:
in your first template do this:
{% for discussion in discussions %}
{% include "discussion.html" }}
{% endfor %}
and the second template has request.user.id in its namespace:
{% if request.session.user.id not in discussion.users_voted %}
user not in list!
{% endif %}