I've read that django creates db_index automatically for all foreign keys. However, does that db_index improve the performance of the reverse lookup as well?
For example, if B has a foreign key to A and I use a.b_set.all(), do I enjoy the performance boost from the db index or not?
And if not, is there a way to make the foreign key reverse lookup faster with db index?
Thanks,
Let's say the you have a simple model structure:
class Author(models.Model):
name = models.CharField(max_length=70)
email = models.EmailField()
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author)
As you mention Book.author already have index, because it's a ForeignKey
Now querying:
author_books = Book.objects.filter(author=a)
or
author_books = a.book_set.all()
produce the exact same query, therefore the book.author index will be used in both situations.
Related
class profiles(models.model):
customer_ID = models.IntegerField().primary_key
Is this the correct way of making a primary key in django??
The correct syntax for primary key is-:
class profiles(models.model):
customer_ID = models.IntegerField(primary_key=True)
No, That is not the correct way of making a primary key in Django, in fact you don't have to specify a Primary key for your model, because django will automatically add a field to hold the primary key for you.
In your settings.py file, you will find a line with:
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
which will automatically creates an 'id' field in all of your models by default. The BigAutoField is a 64bit integer that automatically increments according to available ids from 1 to 9223372036854775807.
class Profile(models.Model):
customer_username = models.CharField(max_length=100)
customer_email = models.EmailField()
the Profile model will have three fields: id, customer_username, customer_email
but, in case you want to overide the primary key, let's say for instane by using UUIDs instead of regular ids, you can overide it as follows:
import uuid
class Profile(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4,editable=False)
customer_username = models.CharField(max_length=100)
customer_email = models.EmailField()
for more details, please refer to the django documentation: https://docs.djangoproject.com/en/4.0/ref/models/fields/#primary-key
Is this the correct way of making a primary key in django??
No. You use an AutoField [Django-doc] for a primary key, since then the values are dispatched by the database, so:
class profiles(models.model):
customer_ID = models.AutoField(primary_key=True, editable=False)
But you do not have to specify a primary key: if you do not specify one yourself, Django will add one with the name id to the model automatically.
I have 2 models, "Listing" and "Watchlist". Watchlist has a primary key that is referencing Listing.
For every "Watchlist" object (that has the same user X) I want to get the according "Listing" entry in form of a QuerySet.
I really struggle with this, because I don't know how to incorporate a for-loop into a queryset request.
class Watchlist(models.Model):
listing = models.ForeignKey(Listing, on_delete=models.CASCADE, related_name="watchlists")
user = models.ForeignKey(User, on_delete=models.CASCADE)
Since listing field has related_name="watchlists", we can filter using related_name like this:
Listing.objects.filter(watchlists__user = X) # X is a user instance.
Note that __ is used for field lookups. __user is for user field lookup.
If listing field does not have any related name, we can filter using model name (Watchlist) with _set to the referencing model.
Listing.objects.filter(Watchlist_set__user = X) # X is a user instance.
I am developing a Django website and I have the following models (simplified):
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
opinions = JSONField(default=default_opinions)
class Author(models.Model):
name = models.CharField(max_length=50, unique=True)
class Book(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE, default='0')
with the opinions field being the opinion that a specific user has of the different authors:
Exemple:
{
Shakespeare: 0.5,
Voltaire: 0.6
}
Then I have a listView BookListView, in which I want to query the Book database, and order them by the opinion the currently logged in user has of their author.
In the previous example it would be all the Voltaire's books first, then the Shakespeare's ones.
So I came up with this in my listView:
def get_queryset(self):
user_opinions = self.request.user.profile.opinions
queryset = Book.objects.order_by(user_opinions[F("author__name")])
return queryset
The problem is the F value is computed after the get_queryset(), so I get a F("author__name") key does not exist error.
I thought about iterating through the dict keys and values but I don't see how that could work since opinions are floats (and thus can take any values).
Thanks in advance ^^
F expressions doesn't support JSON field lookup,that's why you are getting error key does not exist, because it is trying to find field named with that and running join on that field,
Can you elaborate more, on what key and value pair are there in json Field
I'm trying to create 'unique_together' meta for a model, but instead of two fields from the current model, one of them is a field of other model (which is a foreign key in the current model):
I want the 'Item' model to have unique_together that contains both its 'identifier' field and the Spec's container_id. a Spec is foreign key in 'Item'.
Tried something like this, but I get "Unresolved reference spec..."
class Spec(BaseModel):
title = models.CharField(max_length=200)
identifier = models.IntegerField(unique=True)
container = models.ForeignKey(Container, related_name='specs')
class Item(SubmittalsBaseModel, BaseModel, DirtyFieldsMixin):
title = models.CharField(max_length=200)
identifier = models.CharField(max_length=200, unique=True)
spec = models.ForeignKey(Spec, related_name='items')
class Meta:
container_id = spec.container
unique_together = ('identifier', 'container_id')
You can't do that.. (at least I think)..
The unique_together clause is directly translated to the SQL unique index. And you can only set those on columns of a single table, not a combination of several tables.
You can add validation for it yourself though, simply overwrite the validate_unique method and add this validation to it.
Docs: http://docs.djangoproject.com/en/dev/ref/models/instances/#django.db.models.Model.validate_unique
Let's assume I have the following models:
class Position(models.Model):
name = models.CharField()
class PositionStats(models.Model):
position = models.ForeignKey(Position)
averageYards = models.CharField()
averageCatches = models.CharField()
class PlayerStats(models.Model):
player = models.ForeignKey(Player)
averageYards = models.CharField()
averageCatches = models.CharField()
class Player(models.Model):
name = models.CharField()
position = models.ForeignKey(Position)
I want to perform the equivalent SQL query using django's ORM:
SELECT *
FROM PlayerStats
JOIN Player ON player
JOIN PositionStats ON PositionStats.position = Player.position
How would I do that with django's ORM? The query isn't exactly correct, but the idea is that I want a single query, using django's ORM, that gives me PlayerStats joined with PositionStats based on the player's position.
I've been working with django for a while now and I have had a pretty rough time figuring out the table joins, but I think I finally understand and I would like to pass this on to others so they may avoid the frustration that I had with it.
Consider the following model.py:
class EventsMeetinglocation(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=100)
address = models.CharField(max_length=200)
class Meta:
managed = True
db_table = 'events_meetinglocation'
class EventsBoardmeeting(models.Model):
id = models.IntegerField(primary_key=True)
date = models.DateTimeField()
agenda_id = models.IntegerField(blank=True, null=True)
location_id = models.ForeignKey(EventsMeetinglocation)
minutes_id = models.IntegerField(blank=True, null=True)
class Meta:
managed = True
db_table = 'events_boardmeeting'
Here we can see that location_id in EventsBoardmeeting is a foreign key for the id in EventsMeetinglocation. This means that we should be able to query the information in EventsMeetinglocation by going through EventsBoardmeeting.
Now consider the following views.py:
def meetings(request):
meetingData = EventsBoardmeeting.objects.all()
return render(request, 'board/meetings.html', {'data': meetingData })
As stated many times before in may other posts, django takes care of joins automatically. When we query everything in EventsBoardmeeting we also get any related information by foreign key as well, But the way that we access this in html is a little different. We have to go through the variable used as the foreign key to access the information associated with that join. For example:
{% for x in data %}
{{ x.location_id.name }}
{% endfor %}
The above references ALL of the names in the table that were the result of the join on foreign key. x is essentially the EventsBoardmeeting table, so when we access x.location_id we are accessing the foreign key which gives us access to the information in EventsMeetinglocation.
select_related() and prefetch_related() is your solution. They work almost same way but has some difference.
select_related() works by creating an SQL join and including the fields of the related object in the SELECT statement. For this reason, select_related gets the related objects in the same database query. But it only works for one-to-one or one-to-many relation. Example is below-
entry = Entry.objects.select_related('blog').get(id=5)
or
entries = Entry.objects.filter(foo='bar').select_related('blog')
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. So prefetch_related will execute only one query for each relation. Example is given below-
Pizza.objects.all().prefetch_related('toppings')
It isn't one query, but it's pretty efficient. This does one query for each table involved, and joins them in Python. More on prefetch_related here: https://docs.djangoproject.com/en/dev/ref/models/querysets/#prefetch-related
Player.objects.filter(name="Bob").prefetch_related(
'position__positionstats_set', 'playerstats_set')
In Django 3.2, the framework automatically follows relationships when using method QuerySet.filter()
# The API automatically follows relationships as far as you need.
# Use double underscores to separate relationships.
# This works as many levels deep as you want; there's no limit.
# Find all Choices for any question whose pub_date is in this year
# (reusing the 'current_year' variable we created above).
>>> Choice.objects.filter(question__pub_date__year=current_year)
This compiles to the following SQL query:
SELECT
"polls_choice"."id",
"polls_choice"."question_id",
"polls_choice"."choice_text",
"polls_choice"."votes"
FROM
"polls_choice"
INNER JOIN "polls_question" ON
("polls_choice"."question_id" = "polls_question"."id")
WHERE
"polls_question"."pub_date" BETWEEN 2020-12-31 23:00:00 AND 2021-12-31 22:59:59.999999
See tutorial here: https://docs.djangoproject.com/en/3.2/intro/tutorial02/
From django.db import connection In your view include the below statement:
cursor = connection.cursor()
cursor.execute("select * From Postion ON Position.name = Player.position JOIN
PlayerStats ON Player.name =
PlayerStats.player JOIN PositionStats ON Position.name = PositionStats.player")
solution = cursor.fetchall()