Which object should hold the many-to-many relationship in Django? - django

I'm just learning Django and have a quick question: I see that when creating a model you can define a many-to-many relationship between two objects. While you assign this relationship to one of the objects, Django actually creates a third table to resolve this M:N relationship. Given this, does it matter which object holds the many-to-many field or can it appear on either of the two, related objects? (coming from a relational DB background, I've got to say that the concept of assigning the M:N to one table feels a bit odd - I'm still not over the idea of not starting with an ER diagram)

No, it doesn't matter. It can go on either of the two.

Related

Adding to a Many to Many relationship given primary keys

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.

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.

Django, multi-table inheritance is that bad?

This isn't really specific to django.
One can model
Place (with location, name, and other common attributes)
- Restaurant (menu..)
- ConcertHall (hall size..)
in two separate tables and let each one hold all the fields they need. (in django world, this is called abstract inheritance)
in three tables, where one holds the common fields and the other two has their own unique fields. (multi-table inheritance in django)
The authors of book Two scoops of Django 1.8 strongly advise against using multi-table inheritance.
Say you want to query places based on it's location and paginate the results (It doesn't have to be a location, can be any other common attribute we want to filter on)
I can see how I can achieve it using Multi-table inheritance.
select place.id from place LEFT OUTER JOIN "restaurant" on (
restuarant.id=place.id) LEFT OUTER JOIN "concerthall" on (
concerthall.id=place.id) where ... order by distance
Is it feasible to do it with abstract inheritance?
According to Django documentation: Model inheritance:
The only decision you have to make is whether you want the parent models to be models in their own right (with their own database tables), or if the parents are just holders of common information that will only be visible through the child models.
I think both possibilities are just tools, equally good tools and it just depends on your use case for their appropriateness. Surely there are specific things to consider for both approaches, and conceptually sometimes multi-table inheritance may be more difficult to comprehend, but other than that this topic just turns to become opinionated.
If you need a single queryset for both models, then it is logical that you consider multi-table inheritance rather than abstract models, because otherwise you would need to get into combining two querysets into one, most probably by using lists as this relevant answer suggests, but you would definitely lose ORM functionality.
It depends on your usecases, but Django ihave a good Database ORM for Database Normalized table structure.
Keeping the base fields in a model and keeping the specifics on another is the best approach in Database Normalization logic because you may have query on different tables and that is not a desired situation. Django relations and reverse relations offers you what you need at this point.
An Example based on yours considering you are using Multi Table Inheritance:
Place.objects.filter(location=x)
Place.objects.filter(location=x, Q(Q(concerthall__hallsize__gt=y)| Q(restaurant__menu=z)))
Place.objects.filter(location=x, concerthall__id__isnull=True)
First will return you all Restaurants and Concert Halls in x.
Second will return you All places which are Concert Halls with hall sizes greater than y or Restaurants with menu z.
Last one is a super magic query that will return you all places in location x which is not a Concert Hall. That is useful when you have many Models inheriting from Place. You can use <model_name>__id for including/excluding tables according to your needs.
You can built great JOINS including many tables and do stick to Database Normalization rules while doing this. You will keep your related data in one place and avoid possible data integrity problems.

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.

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.