django select_related for multiple foreign keys - django

How does select_related work with a model which has multiple foreign keys? Does it just choose the first one?
class Model:
fkey1, fkey2, fkey3...
The documentation doesn't say anything about this, at least not in where the method is specified.
NOTE: Maybe I'm the only one who will get confused. I guess select_related is just a performance booster (I can see that) but I had the wrong idea that it was something else.

You can use select_related in a chain as following
Comment.objects.select_related('user').select_related('article').all()

If your model has multiple foreign keys you can:
Call .select_related(), that will “follow” all non-null foreign-key relationships
Call .select_related('foreign_key1', 'foreign_key2', ...), that will “follow” only the foreign-key provided as arguments.
Note that "to follow a FK relationship" means selecting additional related-object data when the query is executed (by performing a SQL join). This will make the main query heavier but can be used to avoid N + 1 queries problem.
According to select_related documentation, the first method (without arguments) is not recommended as "it is likely to make the underlying query more complex, and return more data, than is actually needed."
If your model has "nested" foreign keys with other models (i.e. Book <>-- Author <>-- Hometown) you can also use select_related as follow:
Call Book.select_related('author__hometown'), that will “follow” the author's foreign-key (in Book model) and the hometown's foreign-key (in Author model).
If your model has many-to-many or many-to-one relations you would like to retrieve from the database, you should take a look at prefetch_related.

On the contrary, the documentation is very clear on the matter. It says that by default all ForeignKeys are followed, but you can give the method a list of fields and it will only follow those relationships.

You can pass foreign keys and even nested foreign keys to the select_related method eg select_related('book__author', 'publisher',) you can add as many foreign keys as you want, if you call select_related() without any argument then it will follow all the foreign key relationship which is not recommended at all because you'll be complicating the query by fetching data you don't need. Finally, from Django documentation "Chaining select_related calls works in a similar way to other methods - that is that select_related('foo', 'bar') is equivalent to select_related('foo').select_related('bar')

Related

Following nested foreign keys while prefetching in Django

I have an information model with deep and complex foreign key relations. and lots of them. Because of it I am trying to use select_related() and prefetch_related() to minimize the number of queries to my DB.
I am having the problem, however, that I cannot figure out a way to make pre-fetching operators follow foreign keys to an arbitrary depth. I am aware of the double underscore operator (__), but that isn't really an option, because I do not know in advance how deep the nesting will be.
So for example let's say I have objects A, B, C,...Z. Any object can have an arbitrary number of foreign keys pointing to any object that appear, say, later on in the alphabet. How can I make sure that, for example, prefetching the foreign key that from A points to B will follow all the foreign keys on B?
My best shot for now was a semi-hard coded approach on the get_queryset() method on the object manager.
Thank you in advance
EDIT:
Ok so an idea on how I am trying to do it at the moment is as follows:
class MyModelmanager(model.Manger):
def get_queryset()
qs = super().get_queryset()
qs = qs.select_related(*thefiledsiwannaprefetch)
return qs
Now in the fields I am prefetching there are foreign keys relations I would like to follow. How do I achieve that (without using '__')?
EDIT 2
Another attempt was the following:
class MyModelmanager(model.Manger):
def get_queryset()
return super().get_queryset().prefetch_related()
I did then overrode the manger of the other models, so that they also performed prefetching in their get_queryset() method. This also didn't work.
From the docs:
There may be some situations where you wish to call select_related() with a lot of related objects, or where you don’t know all of the relations. In these cases it is possible to call select_related() with no arguments. This will follow all non-null foreign keys it can find - nullable foreign keys must be specified. This is not recommended in most cases as it is likely to make the underlying query more complex, and return more data, than is actually needed.

How to enforce database constraints between two foreign keys on a join table?

I am trying to enforce a constraint between two foreign keys on a join table, and I don't know whether I can do it using the database, or whether I should do it through my app, or my ORM.
Here are my tables:
Dataset
Tag
- Dataset: FK
- name: string (eg: "park", "church", etc)
Place
- Dataset: FK
- latitude
- longitude
PlaceTag (my join table)
- Tag: FK
- Place: FK
- note: string (eg: "this place is my favorite park")
I want to enforce the constraint that each PlaceTag has a Tag and a Place that belong to the same Dataset. Should I do this using the database, or my app? Or should I re-structure my models to enforce this constraint more easily?
FWIW, this is an open-source project, and my PR for creating these tables is up here: https://github.com/mapseed/api/pull/161/files The project is using Django, if that helps.
One way of "enforcing" (note the quotation marks) this in Django would be to override the PlaceTag's save() method. In there you can raise an exception whenever self.place.dataset != self.tag.dataset. Yet you should note that there are situations in which Django will not call the custom save() method of a model:
When calling the update() method on a queryset. This method is meant for bulk updates and hence, for performance reasons, proceeds with the update directly at a database level (reference).
Inside (data) migrations custom save() methods are not available.
In these two situations the approach I propose will not be useful to enforce the constraint (hence the quotation marks at the beginning). This is of course not the same and not as strong as enforcing this at a database level. Anyway I don't think there is a portable way (i.e. available in any or most SQL database engines) of enforcing such a condition since checking it will require a join on other tables, yet I may be wrong on this one.

Behavior of querysets with foreign keys in Django

When a model object is an aggregate of many other objects, whether via Foreign Key or Many To Many, does iterating over the queryset of that object result in individual queries to the related objects?
Lets say I have
class aggregateObj(models.Model):
parentorg = models.ForeignKey(Parentorgs)
contracts = models.ForeignKey(Contracts)
plans = models.ForeignKey(Plans)
and execute
objs = aggregateObj.objects.all()
if I iterate over objs, does every comparison made within the parentorg, contracts or plan fields result in an individual query to that object?
Yes, by default every comparison will create an individual query. To get around that, you can make use of the select_related (and prefetch_related the relationship is in the 'backwards' direction) QuerySet method to fetch all the related object in the initial query:
Returns a QuerySet that will automatically “follow” foreign-key relationships, selecting that additional related-object data when it executes its query. This is a performance booster which results in (sometimes much) larger queries but means later use of foreign-key relationships won’t require database queries.
Yes. To prevent that, use select_related to fetch the related data via a JOIN at query time.

How do I express a Django ManyToMany relationship?

I'm hitting a wall here and I know this is a simple question, but I was unable to find it here.
In an ER diagram, what would the relationship be between two objects that have a ManyToMany relationship, in terms of the intermediary table?
Example:
item ---- item_facts ---- fact
I feel like it should be one to one but I'm not completely sure.
user --many2many-- group
user 1----n user_group n---1 group
In django documentation it states that
A many-to-many relationship. Requires a positional argument: the class to which the model is related. This works exactly the same as it does for ForeignKey, including all the options regarding recursive and lazy relationships.
Behind the scenes, Django creates an intermediary join table to represent the many-to-many relationship. By default, this table name is generated using the name of the many-to-many field and the model that contains it. Since some databases don't support table names above a certain length, these table names will be automatically truncated to 64 characters and a uniqueness hash will be used. This means you might see table names like author_books_9cdf4; this is perfectly normal. You can manually provide the name of the join table using the db_table option.
And ForeignKey definition is like:
A many-to-one relationship. Requires a positional argument: the class to which the model is related.
So,ManyToMany relations created by django are creating intermedıary tables that are 1 to N.
Not sure what the question is here. You say that the two objects have a many-to-many relationship.
If two objects (entitied, tables) have a many-to-many relationship, whether you include the intermediate table in the diagram or not, is irrelevant. They still have a many-to-many relationship.

many-to-many relation with extra fields - how to circumvent uniqueness

I have the following task.
Having three models Project, User and PurchaseOrder. I want to be able to create a membership for a User in a Project. A User can be a member in arbitrary Projects. This can be solved with a
ManyToManyField.
Additionally a membership should reference to a PurchaseOrder, since I want to assign the working hours to specific PurchaseOrders.
I think this could be solved by using a through-table for the ManyToManyField, and defining a ForeignKey to the PurchaseOrder model. Thus I would have for each membership a reference to a PurchaseOrder.
In reality a membership for a project will stay active, whereas after the money is spend for a PurchaseOrder, a new PurchaseOrder has to be assigned to the membership. This would also be easy by just updating the ForeignKey to a new PurchaseOrder.
But now my question:
I want to keep the old Project-membership-PurchaseOrder-Relation (data row in the membership table, for history tracking), set it to disabled and add a new Project-membership-PurchaseOrder-Relation, which would have the same ForeignKey to User and Project, but a different to the PurchaseOrder, and a flag set to enabled.
Is this a valid approach, will this work, will it be possible to circumvent uniqueness (or is there no uniqueness for the ManyToManyField by definition), or do you have a better idea how to do this?
When I read through this I can't figure out why even bother with the Many-to-Many relation for the Member > PurchaseOrder.
I would make it a One-to-Many relation for Member > PurchaseOrder and a Many-to-Many relation Member > Project, as the Membership appears to be the primarykey for it all.
In that way, you don't have to update any keys. Then I would create a fourth Model, having a Many-to-Many relation keeping track of the purchases. Adding the Membership prim. key and the PurchaseOrder prim.key.