I'm try to decreasing queries to single query but queryset returns 2 queries. I tried some method but nothing changed
Models.py
class Match(models.Model):
...
participant = models.ManyToManyField(TournamentTeam, through='MatchParticipant')
...
class MatchParticipant(models.Model):
match = models.ForeignKey(Match, on_delete=models.SET_NULL, null=True, blank=True)
team = models.ForeignKey(TournamentTeam, on_delete=models.SET_NULL, null=True, blank=True)
views.py
queryset = Match.objects.prefetch_related(
Prefetch(
'participant',
queryset=MatchParticipant.objects.select_related(
'match','team'
),
),
).select_related('some_foreignkey',).get(slug=slug)
From django official documentation:
prefetch_related, on the other hand, does a separate lookup for each
relationship, and does the ‘joining’ in Python. This allows it to
prefetch many-to-many and many-to-one objects, which cannot be done
using select_related, in addition to the foreign key and one-to-one
relationships that are supported by select_related.
That's why you get 2 queries being made to your database. That being said, doing so makes you sure you won't get into a N+1 query problem for that query.
Going further
Some profiling/debug tools (ex: Django debug toolbar) can help you troubleshoot complicated queries on your page before it's too late. Also, if you're a pytest-django user, you might want to have a look at django_assert_num_queries. With "native" django test case, have a look at assertNumQueries.
Related
I've got a model called Couple. Couple describes the relationship between two Auth.User records.
class Couple(models.Model):
created = models.DateTimeField(auto_now_add=True)
partner_one = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING, related_name='partner_one')
partner_two = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING, related_name='partner_two')
I'm having trouble referencing the partner in a reliable from their user model because now I need to know which is partner one, partner two. This is obviously not an elegant solution. Is there a better way to achieve what I'm trying to do here?
I'm trying to better understand how to write some more advanced queries. I'm used to the basic Model.objects.all() and .filter(), etc, but I'm having trouble writing out queries that are annotated.
Here is my model setup (I have removed any additional fields that are not necessary for this question):
from django.db import models
class Client(models.Model):
name = models.CharField(max_length=100)
class Project(models.Model):
client = models.ForeignKey(Client, on_delete=models.CASCADE)
class Campaign(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name="campaigns")
class Advertisement(models.Model):
campaign = models.ForeignKey(Campaign, on_delete=models.CASCADE, related_name='advertisements')
What I need is a query that shows how many Advertisements each client has, ordered by most to least.
I'm trying to follow the docs from here: https://docs.djangoproject.com/en/4.0/topics/db/aggregation/ but their annotation example isn't as nested, the publisher model example is a foreign key on the book object, where as with mine the client is a foreign key of a project, which is a foreign key of a campaign, which is a foreign key of advertisements.
Any advice on how to drill down through the layers of the models to get the info I need?
On Django ORM, the joins that navigate the relationships are done with __.
You can keep on nesting the relationship traversal.
This would make your query something like the following.
Client.objects.annotate(num_advertisements=Count('project__campaigns__advertisements'))
The names campaigns and advertisements have to be used because of the related_name you set.
I am using Django, Python 3.7, and PostgreSQL 9.5. How do I mark up my model such taht a cascading foreign key constraint gets generated? I currently have this in my models.py file ...
class ArticleStat(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE, )
When I run make migrations my_project in the management console, it produces a file that contains this ...
migrations.CreateModel(
name='ArticleStat',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
...
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='my_project.Article')),
],
),
However, when I run the migration (using migrate my_project 0001), the resulting foreign key does not contain a cascade delete constraint. This is what the description looks like in PostgreSQL ...
"my_project_articlesta_article_id_a02a5add_fk_my_project" FOREIGN KEY (article_id) REFERENCES my_project_article(id) DEFERRABLE INITIALLY DEFERRED
How else can I get my models.py file to output a cascading delete foreign key constraint?
As described in the django documentation of ForeignKey django merely emulates this behaviour instead of deferring it to the database.
Django emulates the behavior of the SQL constraint ON DELETE CASCADE and also deletes the object containing the ForeignKey.
So to answer your question: This is expected behavior and will still work just like you would expect it to work on the database level.
How else can I get my models.py file to output a cascading delete foreign key constraint?
You can't as django currently does not support this feature. However there is a ticket discussing to add it: https://code.djangoproject.com/ticket/21961
Edit for further clarification on how to enforce this at database-level
While I highly recommend to just let django handle this for you there might be reasons not to do so.
To opt out of database table creation or deletion operations you can set Options.managed to False in the Meta class of ArticleStat. That would however also mean that you are now responsible to do the migrations manually e.g. writing the CREATE TABLE statement to define the table including the foreign key constraint (which you thus have full control over now). Another consideration to take into account is that you should instruct django to not do anything on deletion of a referenced Article object anymore (as your database is now responsible for that). That can be ensured by setting on_delete to models.DO_NOTHING.
Put together your ArticleStat would now look like this:
class ArticleStat(models.Model):
article = models.ForeignKey(Article, on_delete=models.DO_NOTHING)
class Meta:
managed = False
A comment about signals prompted me to revisit this and list some pitfalls to consider.
opting out means opting out of django signals as well. In particular pre_delete and post_delete won't be fired for cascaded objects anymore.
As mentioned in the ticket description mixing database- and django-cascading won't play nicely together.
If a model A references a model B using CASCADE_DB, but model B
references model C using regular CASCADE, a deletion of A won't
cascade all the way to C.
That being said I couldn't find any definite proof of why django is handling this the way it does currently.
I have a model called SimplePage in which I have this line:
category = models.ForeignKey('Category', related_name='items',
blank=True, null=True)
I assumed this will allow me to have SimplePage instances that do not have a Category.
But for some reason, when I try to create a SimplePage in the Admin with no Category, I get:
IntegrityError at /admin/sitehelpers/simplepage/add/
sitehelpers_simplepage.category_id may not be NULL
What is this?
Could it possibly be that you added the null=True attribute after doing the syncdb for that model? Django won't change database tables, only create them. Check in your database if NULL is allowed for that column and change it manually.
Edit: starting with Django 1.7, this answer and the comments are not really valid anymore, since Django gained a fully featured migration framework.
I have a model Model with a m2m field :
user = .. fk user
...
watchers = models.ManyToManyField(User, related_name="boardShot_watchers", null=True)
How do i select all distinct Users involved in this watchers relationship for all my entries of type Model ?
I dont think there is an ORM way to access to intermediary M2M table.
Greg
Not in your current model. If you want to have explicit access to the joining table, you need to make it part of the Django object model. The docs explain how to do this:
http://www.djangoproject.com/documentation/models/m2m_intermediary/
The admin and other django.contrib* components can be configured to treat most fields the same as if they were just model.ManyToMany's. But it will take a little config.