Adding to a Many to Many relationship given primary keys - django

Given primary keys to two different objects with a many to many relationship, what is the most effective way to add the relationship that results in the least amount of database hits?
I'm thinking something like the below, but it results in hitting the database twice.
ob = A.objects.get(pk=pk_a)
ob.B.add(pk_b)
Is it possible to only hit the database once?

Is it possible to only hit the database once?
Yes. You can just create a model object of the through model, like:
A.b.through.objects.create(a_id=pk_a, b_id=pk_b)
Given the model is A, and b is the name of the ManyToManyField.

Related

Postgresql + Django: What is better between same strings or a many to many relationship?

I'm currently designing my data base using postgresql with Django and I was wondering: What is best practice - having several instances of the same model with the same value or a many to many relation ship?
Let me elaborate. Let's say I'm designing a store. The store sells items. Items can have one or many statuses (e.g. ordered, shipped, delivered, paid, pre-ordered etc.).
What would be a better practice:
Relating the items to their status via a many-to-many relationship, which will lead to one status having hundreds of thousand and later millions of relations? Will so many relations become problematic?
Or is it better for each item to have a foreignkey to their statuses? So that each status only has one item. And if I would like to query all the items that have the same status (e.g. shipped), I would have to iterate over all statuses with a common name.
What would be better, especially for the long term?
I would recommend going with a many-to-many relationship.
Hundreds of thousands or even millions of relations should not be a problem. The many-to-many relationship is stored as a table with id, item_id, status_id. SQL will be performant at querying the table either by status_id or item_id even if the table gets big. This is exactly the kind of thing it was built to handle.
Let me elaborate. Let's say I'm designing a store. The store sells
items. Items can have one or many statuses (e.g. ordered, shipped,
delivered, paid, pre-ordered etc.).
If many people will have this many itens you should use manytomany relations, better let django handle with this "third table", since this table just hold ids you can interate over them using reverse lookup, i do prefer using many to many instad of simple foreignkeys.
In your case, who you will handle when your User will hold many itens? like what if my User buy one potato and 2 bananas? you will duplicate the tuple in your User Table to tell "here he have the potato and in this second one he have the banana"? so you will be slave of Disctinct attribute while you still dirtying your main table User
...
class Item(models.Model):
...
class User(models.Model):
items = models.ManyToMany(Item)
So when i query my Item and my User will only bring attributes related to them... while if you use item inside of User Model you will have multiple instances of same user.
So instead of use User.items.all() you will use User.objects.filter(id=id)and them items = [user.item for user in User.objects.filter(id=id)]
Look how complex this get and makeing your database so dirty

Why Many to Many relationship with self can't be symmetrical

I'm trying to make a model with many to many relationship to itself and this relationship will also have a specific table that will store some information about the relationship, but I'm running into some problems.
I tried to make a many to many relationship with diferent models like the Django docs say, and it's worked fine in some other point of my application. But now I tried to do something like this:
Let's say that I want a model that represents an object (called Item) that is made by other items and also is used to make some other items. For instance, an object Door is made by wood and lock, but Door will also be used to make a House. I thought in something like this for my models
class Item(models.Model):
name = models.CharField(max_length=100)
items = models.ManyToManyField("self",through='IsMadeBy')
class IsMadeBy(models.Model):
itemResult = models.ForeignKey('Item', related_name='itemResult')
itemPart = models.ForeignKey('Item', related_name='itemPart')
amountUsed = models.PositiveIntegerField()
I'm getting the error message:
Many-to-many fields with intermediate tables must not be symmetrical.
So, adding the argument
symmetrical=False
to my relationship the error stops.
With that being said, I want to know how this really works under the hood. For intance, what the symmetrical means in this context in a database level? I would appreciate if anyone could give examples maybe using SQL statements, since right now my brain can't see the overall situation and really learn this concept of symmetrical relationship in a many to many relationship with self.
Look at the Django docs:
https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.ManyToManyField.symmetrical
With symmetric relationship if i'm your friend then you my friend too - in Django terms, you have one relation.
With non-symmetric direct and reverse relations can be different, you have related_set. For example, if i'm you manager, you not my manager at the same time, but manager via employee_set can have many employees.

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.

Django - Fetching foreign key

Model A has a ForeignKey to model B - I would like to fetch A instances and compare them to each other where the key to B is one of the comparison parameters.
Django defers fetching B related info, so if I want to optimize my code and fetch in advance the info I need I can do one of the following:
Use .select_related('B') - which will fetch all related B instances
Use .select_related('B__id') - which will fetch only the ids of all related B instances
AFAIK both require a join, where all I really needed was A.B_id which is a column in the database, as that is all I wanted to compare.
Am I missing something straightforward here?What am I missing here? Can I fetch A.B_id directly? Is
Firstly, your assertion is wrong: select_related('B__id') doesn't do anything. The double-underscore in a select_related call is only for following subsequent joins: so if B had a ForeignKey to C, select_related('B__C') would follow the second JOIN as well.
Secondly, I'm confused by your optimisation requirement. As you say, you just want B_id: so no JOIN is required, and neither is any optimisation. If you just get your A objects in the normal way, you can refer to the b_id field on each of them directly:
a_objects = A.objects.all()
for obj in a_objects:
print a.b_id
Here only a single db call is made, with no JOINs.

unidirectional one-to-many and many-to-may in django

I'm new to django.
I have 2 simple objects, lets call them - File and FileGroup:
- A FileGroup can hold a list of files, sorted according to an 'order' field.
- Each file can be associated with multiple groups.
so basically, the db tables would be:
1) File
2) File_Group
3) File_Group_Mapping table that has a column named "order" in addition to the fk to the file and file group.
There is a many-to-many relationship here, but the File object is not supposed to be aware of the existence of the FileGroup (doesn't make sense in my case)
My questions -
Is there a way to create a unidirectional many-to-many/one-to-many relationship here? How can I model it with django?
I couldn't find a way to make it unidirectional via django.
I saw a solution that uses something like -
class FileGroup(...):
files = models.ManyToManyField(File, through='FileGroupMapping')
but this will make the File object aware of the FileGroup.
I can also do this via mapping the File_Group_Mapping table in the models file like this -
class FileGroupMapping(...):
files = models.ForeignKey(File)
groups = models.ForeignKey(FileGroup)
order = models...
What is the best way to do this via django?
Thanks
I am also much of a hibernate user. I totally understand what you are looking for, just try using the attribute "symmetrical = False" in your many to many relation ship this would make the relationship unidirectional.
class FileGroup(models.Model):
files = models.ManyToManyField(File, symmetrical = False)
This should do the trick!
Your two approaches are identical. Behind the scenes, Django creates a lookup table for a ManyToManyField. From the ORM perspective, you can put the ManyToManyField on either model, although it makes a difference in the admin, and if you wish to use the 'limit_choices_to' option. Using 'through' lets you add columns to the lookup table to further define the relationship between the two models, which is exactly what you've done by manually creating the lookup table.
Either way, you can still 'get' the FileGroup that a particular File belongs to, as Django querysets will follow a FK relationship bidirectionally.