Django SQL query duplicated - python

I have a models.py file containing this:
class Entry(models.Model):
text = models.TextField(default='')
time_created = models.DateTimeField(auto_now=False, auto_now_add=True)
time_updated = models.DateTimeField(auto_now=True, auto_now_add=False)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
abstract = True
#rest of code ...
class Question(Entry):
def get_absolute_url(self):
return reverse('view_question', args=[self.id])
class Answer(Entry):
question = models.ForeignKey(Question, on_delete=models.CASCADE, default=None)
By using Django-debug-toolbar I found out that I am duplicating many SQL queries.
What I am trying to do is get the last 5 questions asked, and display them in my home page, along with some more data containing Answers to those questions, the user who provided the answer, avatar of that user and etc and etc...
I am currently doing that with this views.py and home.html template:
views.py:
#login_required(login_url="accounts/login/")
def home_page(request):
last_five = Question.objects.all().order_by('-id')[:5]
last_five_in_ascending_order = list(reversed(last_five))
return render(request, 'home.html', {
'question_list': last_five_in_ascending_order,
})
home.html:
{% for question in question_list %}
{{ question.text }}
{% if question.answer_set.all %}
{% for answer in question.answer_set.all %}
<img src="/media/{{ answer.created_by.userprofile.avatar }}" alt="">
<a href="{% url 'profile' answer.created_by.id %}">
{% firstof answer.created_by.get_full_name answer.created_by.username %}
</a>
{{ answer.time_passed }}
{{ answer.text | safe }}
{% endfor %}
{% endif %}
{% endfor %}
How can I decrease my sql queries to the database?
And should I really be worried about these things? Do they make a difference for a website with little user activity?

What you have here is table inheritance rather than a simple many to one relationship and that makes like really complicated. I don't really see why a simple setup like this needs table inheritance here. Modifying your models like this works just as well
class Entry(models.Model):
text = models.TextField(default='')
time_created = models.DateTimeField(auto_now=False, auto_now_add=True)
time_updated = models.DateTimeField(auto_now=True, auto_now_add=False)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
abstract = True
#rest of code ...
class Question(models.Model):
entry = models.OneToOneField(Question)
def get_absolute_url(self):
return reverse('view_question', args=[self.id])
class Answer(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE, default=None)
entry = models.OneToOneField(Question)
To solve your issue of many queries being executed. We now resort to select_related
Question.objects.select_related('entry)
Answer.objects.select_related('entry', 'question')
The debug toolbar will now show that repetitive queries have been eliminated.
should I really be worried about these things? Do they make a
difference for a website with little user activity?
No all those queries are attempts to fetch related objects by their primary key. In any database, a lookup on a single item with a primary key is O(log (N)). But the base of the logarithm is usually a so you need not be concerned.
If you fetched them all at once it would be of course be faster since you are now having a single O(log (N)) operation with larger constants. But whether the difference is noticeable on a small site is debatable.

Related

django - having trouble selecting values based on FK relationship in views.py

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

How do i query for comment in Django

I am new in Django and I have been following tutorials online. I am having problem on how to display the comments.
How do i query for comments in views, so i can display comments for a particular post.
Model:
class Post(models.Model):
poster_profile = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE, blank=True,null=True)
image_caption = models.TextField(blank=True, null=True)
class Comments (models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE, blank=True,null=True)
commented_image = models.ForeignKey(Post, on_delete=models.CASCADE, null=True, blank=True)
comment_post = models.TextField()
Views.py:
def home_view(request):
comment = Comments.objects.all() #This is getting all comment in all post, how do i query for comment in a particular post.
context{'comment':comment}
return render(...)
Template:
{% for com in comment %}
<p>{{ com.comment_post }}</p>
{% endfor %}
You can do
post = Post.objects.get(id=1)
comment = post.comments_set.all()
Following Relationships "Backward"
If a model has a ForeignKey, instances of the foreign-key model will have access to a Manager that returns all instances of the first model. By default, this Manager is named FOO_set, where FOO is the source model name, lowercased. This Manager returns QuerySets, which can be filtered and manipulated as described in the “Retrieving objects” section above.
Note this behaviour can be overridden.
You can override the FOO_set name by setting the related_name parameter in the ForeignKey definition. For example, if the Entry model was altered to blog = ForeignKey(Blog, on_delete=models.CASCADE, related_name='entries'), the above example code would look like this:
Edit #2:
views.py:
def home_view(request):
posts = Post.objects.all().reverse()[5]
context{ 'posts': posts, }
return render(...)
Now in your templates you can do something like:
{% if posts %}
{% for post in posts %}
{{ post.image_caption }}
{% for comment in post.comments_set.all %}
{{ comment.comment_post }}
{% endfor %}
{% endfor %}
{% endif %}
First of all, take a look at Django queryset documentation, especially select_related for this kind of issues (to reduce number of queries to database). I didn't try but following snippet must work.
class Post(models.Model):
poster_profile = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
image_caption = models.TextField(blank=True, null=True)
class Comments (models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
commented_image = models.ForeignKey(Post, related_nam="comments", on_delete=models.CASCADE)
comment_post = models.TextField()
...
def home_view(request):
post = Post.objects.filter(id=request.data.get('post_id')).select_related("comments") # specify the post anyhow ..
comments = post.comments
context{'comment': comments}
return render(...)
def home_view(request):
particular_Post= Post.objects.get(id=1)
comment = Comments.objects.get(Post=particular_Post)
context{'comment':comment}
return render(...)
to understund Query in django i suggest U to start by
python manage.py shell
and Import you're Models

Django: Problem with Understanding Objects in For Loop

I am a beginner in Django. I am building a Django app, named PhoneReview. It will store reviews related to the latest mobile phone. It will also display phone brands, along with the associated phone models. I have managed to do some protion of the app. Right now, I am a bit confused with a line of code.
I have a code like this in one of my template files:
{% extends 'gamereview/base.html' %}
{% block title%}
Detail
{% endblock %}
{% block content %}
<h3>This is the review for {{game.title}} </h3>
<ul>{% for review_item in game.review_set.all %}
<li>{{ review_item.review }}</li>
{% endfor %}
</ul>
{% endblock %}
I don't understand this portion:
<ul>{% for review_item in game.review_set.all %}
What does this line mean?
Here are the codes in models.py:
from django.db import models
from django.template.defaultfilters import slugify
# Create your models here.
class Tag(models.Model):
label = models.CharField(max_length=20)
def __str__(self):
return self.label
class Game(models.Model):
title = models.CharField(max_length=100)
developer = models.CharField(max_length=100)
platform = models.CharField(max_length=50, default='null')
label_tag = models.ManyToManyField(Tag)
slug = models.SlugField(max_length=150, default='null')
def __str__(self):
return self.title
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super().save(*args, **kwargs)
class Review(models.Model):
game = models.ForeignKey(Game, on_delete=models.CASCADE)
review = models.CharField(max_length=1000)
date = models.DateField(auto_now=True)
slug = models.SlugField(max_length=150, default='null')
def __str__(self):
return self.review
def save(self):
super(Review, self).save()
self.slug = '%i-%s' % (
self.id, slugify(self.game.title)
)
super(Review, self).save()
Here are the codes in views.py:
from django.views import generic
from .models import Game
class GameListView(generic.ListView):
template_name = 'gamereview/gamelist.html'
context_object_name = 'all_games'
def get_queryset(self):
return Game.objects.all()
class ReviewView(generic.DetailView):
model = Game
template_name = 'gamereview/review.html'
Here are the codes in urls.py:
from . import views
from django.urls import path
app_name = 'gamereview'
urlpatterns = [
path('gamereview/', views.GameListView.as_view(), name='gamelist'),
path('gamereview/<slug:slug>/', views.ReviewView.as_view(), name='review'),
]
I am a bit confused by this line: <ul>{% for review_item in game.review_set.all %}. Would you please help me to clarify?
Look at the models; there is a Game class. Apparently you receive an instance of that class in your template under the name game.
The Game class is referenced as a foreign key by Review class. This, due to the way Django ORM works, gives Game class a reverse link .review_set; it contains all review objects that refer to the particular game. Hence game.review_set.
That .review_set attribute is not a list but a DB result set. You can filter it, sort it, etc, but in your case you just fetch all the records from it. Hence game.review_set.all.
Please take some time to read an intro to how Django works, a number of things there cannot be derived from mere common sense.
I'd like to add something that was helpful for me when referencing the related database result set:
In your class, you can specify a "related_name" for the foreign key and use it to reference that result set in your template.
For instance,
class Review(models.Model):
game = models.ForeignKey(Game, on_delete=models.CASCADE, related_name="game_reviews")
And, now you reference this in your template (assuming the context name is all_games):
{% for x in all_games %}
{% all_games.title %} </br>
<ul>{% for review_item in game.game_reviews.all %}
<li>{{ review_item.review }}</li>
{% endfor %}
</ul>
{% endfor %}
Of course, you can further simplify the related_name to just "reviews" and shorten your code that much more. And, it's all very logical when you read it.

Django HTML 'for loop' not displaying any of the objects in a set

so I am having trouble with my for loop which is meant to display all the tasks allocated to a user. There are four tasks allocated to them, as you can see on this imgur link . However it does not show them on the site.
Here is my detail.html file:
<h1>{{user.fullName}}</h1>
<h4>Username: {{user.userName}}</h4>
<ul>
{% for i in userName.i_set.all %}
<li> {{ i.taskName }} </li>
{% endfor %}
</ul>
Here is my models.py:
from django.db import models
class User(models.Model):
userName = models.CharField(max_length=30)
password = models.CharField(max_length=100)
fullName = models.CharField(max_length=50)
def _str_(self):
return self.fullName
class Task(models.Model):
userName = models.ForeignKey(User, on_delete=models.CASCADE)
taskName = models.CharField(max_length=30)
scores = models.CharField(max_length=100) # score should default be set to x/10 until changed
def _str_(self):
return str(self.userName) + ' - ' + self.taskName
I know its a pain to help someone you don't know but I'd really appreciate it as ive been working on this problem for an hour and managed nothing and I'm really eager to learn what the problem is... thanks so much and have a nice day!
Based on the code you have provided your for loop code is completely wrong, try to change it to:
{% for i in user.task_set.all %}
More better is to use related_name in ForeignKey for ease of access:
userName = models.ForeignKey(User, on_delete=models.CASCADE, related_name='tasks')
Then you can access related tasks for user in template like this:
{% for i in user.tasks.all %}

Displaying a list of table entries in Django

I have a database table that allows you to enter details about a particular person. How can i then list all the entries of that table to show all the people added to the database.
urls.py
url(r'^accounts/loggedin/locations/all/$', 'assessments.views.locations'),
url(r'^accounts/loggedin/locations/get/(?P<location_id>\d+)$', 'assessments.views.location'),
url(r'^accounts/loggedin/locations/create/$', 'assessments.views.create'),
models.py
class People(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
view.py
def people(request):
return render_to_response('dashboard/people.html', {'people': People.objects.all})
people.html
<body>
<h1>People</h1>
{% for p in people %}
<h1>{{ people.first_name }}</h1>
{% endfor %}
</body>
</html>
Two problems:
you should call all() to get the results, note the ():
People.objects.all()
in the template, you should use {{ p.first_name }} instead of {{ people.first_name }} since you are iterating over people variable which is a QuerySet - a list of objects, basically
it is same question with this link
If you had the Question model below,
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField(verbose_name='date published')
Through this code, you can see all entries (or fields, features).
fields = Question._meta.get_fields()
thanks.

Categories

Resources