Django symmetrical ManyToMany field on 2 columns - django

In my model, how do I exlicitly state that I want a ManyToMany relationship with another column to be symmetrical, so that when calling an object_set from each object, it can go through the same database table to find the relationships?
An example
class Person(models.Model):
name = models.CharField(max_length=100)
employer = models.ManyToManyField(Organization)
class Organization(models.Model):
name = models.CharField(max_length=100)
Do I need to create a second ManyToManyField in the Organization class, in order to do something like
org1.person_set.all()
to get all persons employed by the organization and
pers1.organization_set.all()
to get all of the organizations a person might work for? Or will the single ManyToManyField symmetrically make the relationships?

You don't need to do anything.
Because the ManyToMany is hosted with Person, it will be:
pers1.employer.all()
and
org1.person_set.all()

Related

How to create a one to many relationship which point from a table to the same table?

I have a table employees with employee_id as a primary key, some of the employees are managers and managers can also have managers.
So I wanted to add a manager_id field to the table employees which is the employee_id of the manager of the employee. I tried to create a one to many relationship between the table and itself but without success.
In the employees class I have added the following:
id_manager = models.ForeignKey(employees, on_delete=models.PROTECT)
NameError: name 'employees' is not defined
I am pretty new to django, any idea how to code this?
Thanks.
The documentation for ForeignKey covers this case explicitly:
To create a recursive relationship – an object that has a many-to-one relationship with itself – use models.ForeignKey('self', on_delete=models.CASCADE).

django many_to_many field deletion

I have two django models which I want to connect using a many to many relationship. See the below example:
class A(models.Model):
name = models.CharField(max_length=1000, unique=True)
class B(models.Model):
name = models.CharField(max_length=1000, unique=True)
aa = models.ManyToManyField(A, related_name='bs', blank=True, null=True)
What I am trying to figure out is what happens if I delete a record of A or of B? What I want to have happen is that the relations in the M2M are deleted, but the other object stays intact. Say a row in A is deleted, then the related rows in B should remain, only the connection through the m2m relationship should be deleted. I can't find it in the Django documentation.
Yes, it is explained in the documentation.
Here is the relevant part:
If we delete a Publication, its Articles won’t be able to access it:
p1.delete()
Publication.objects.all()
<QuerySet [<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>]>
a1 = Article.objects.get(pk=1)
a1.publications.all()
<QuerySet []>
If we delete an Article, its Publications won’t be able to access it:
a2.delete()
Article.objects.all()
<QuerySet [<Article: Django lets you build Web apps easily>]>
p2.article_set.all()
<QuerySet []>
In your case Django will create tables for the models A and B as well as an intermediary table, which is not reflected in the models. When you delete an object instance (a record) of the class A, the associated row in the table A will be deleted, as well as all associated rows in the intermediary table. The table B won't be affected.
By making a m2m relation you're basically making another table with a foreign key to model A and another one to model B (Django does it for you).
By default on_delete is set to models.CASCADE which means if you delete a row in any of those models, the relation will be deleted too. (you can confirm it by deleting a row in your admin page which you'll see a message with list of all relationship that they will be deleted too)
You can make a table yourself to manage these things and connect that table to the model with through=. It's good way to manage m2m relations in a way you actually want.
Doc: Many-to-many relationships

On two related models, which one should contain the definition of the relationship?

First of all, yes: I've read Django's foreign key and many-to-many documentation, but I'm still not 100% clear on how to implement relationships on a practical level, especially regarding the hierarchy of the relationships.
One-to-one
I am aware of how to form one-to-one relationships. However, on a more conceptual level, which model should contain that reference to the other one? Let's say I have a Citizen, and a Passport. Now, it's obvious that one Citizen can only have a single Passport and viceversa, but, ideally, should the Citizen contain a field referencing to his Passport, or should the Passport model contain a reference to the Citizen it belongs to?
Many-to-many
For the sake of simplicity, let's say I have a Person model and a Trip model (Trip as in going out on a trip somewhere). Many Persons can participate in a single Trip. Or in other words: a Person can participate in many Trips and in any single Trip, a lot of Persons can participate. This looks like a many-to-many relationship, but, again, ideally, which model should contain the definition for the relationship, the Person with a trips field or the Trip with a participants field? And why? Does it even make any practical difference?
Thank you.
This depends on your business logic. As a rule of thumb I'd suggest to think about the admin app. How would you like to add new objects?
When adding new objects, how would you like to add related objects?
Let's say you have these models:
Citizen(models.Model):
name = models.CharField()
Passport(models.Model):
number = models.CharField()
citizen = models.OneToOneField('Citizen', related_name='passport')
When adding new passport object, you have the possibility to add new citizen, if it doesn't yet exist. Since this doesn't look very logical to me, I'd change the relation as:
Citizen(models.Model):
# other fields
passport = models.OneToOneField('Passport', related_name='citizen')
Now we can add a new citizen object in the admin and add the related passport object within the same page.
If you use the admin app, this should lead you to more ergonomical design.
EDIT: expand with many-to-many example
Better example for a m2m relation would be StackOverflow - there are questions and tags. A question has many tags, and a tag has many questions. Let's say the models look like this:
Question(models.Model):
title = models.CharField()
body = models.TextField()
author = models.CharField()
tags = models.ManyToManyField('Tag', related_name='questions')
Tag(models.Model):
name = models.CharField()
Why do we put the relation in Question? This should be very logical - when creating a new question you'd like to set the tags for it. When creating a new tag you don't care about any questions associated with it. You can create a tag and later when creating questions, associate them with the tag.
If a tag doesn't exist yet you can add it from the admin, when adding a new question.
I hope this second example is more palpable.
The theory behind this is called database normalization which is a ladder of best practices you should look up if you want to know more about how to structure your data.
The third form tells us that:
"[Every] non-key [attribute] must provide a fact about the key, the whole key, and nothing but the key."
So in the case of ForeignKey fields it should be on the Child model, because it doesn't tell us anything about the parent, but it does tells us what parent the child belongs to.
The mental model that you should have is Parent and Child. Every relationship has two models. So think of one as the Parent model or the Primary model and think of the other one as the Child model or the Secondary model.
NOTE: Always put your relationship field in the CHILD model.
Here is how I would solve your problems:
For the first one, I will have a mental model that Citizen is the Parent and Passport is the child.
class Citizen(models.Model):
name = models.CharField(max_length=255)
info = models.TextField()
class Passport(models.Model):
owner = models.OneToOneField(Citizen)
unique_no = models.CharField(max_length=30, unique=True)
For the second problem, do the same. I would choose Person as the parent model and Trip as the child model.
class Person(models.Model):
name = models.CharField(max_length=255)
info = models.TextField()
class Trip(models.Model):
person = models.ManyToManyField(Person)
info = models.TextField()
If you have sqlitebrowser, you can use that to open your database and check what tables were created according to your models. Then, you will have a clearer idea as to how Django sees your models.

Django. Many-To-Many Field for form, but not for model

I have DB that should have one field with type Many-To-Many, but it is not and I can't change this.
For example I have a list of students and a list of subjects. Subject should be many-to-many field in students table, but as i said it is not. Students table doesn't have this field at all. But there is still another table students-subjects that contains subject_id-student_id items.
How can I embed in student form some kind of subject field that could change students-subjects data when I save a student in DB? The problem is that I can't change the DB structure so need to make it only with help of Django.
There is no such thing as a 'Many-to-many field' for a database-table like you might know it of foreign keys. In database representation many-to-many relationships are realized by using an extra table that links the primary keys (usually the ids) of the records you want to set in relation. In your case that is the table students-subjects. Django uses this table when you define a many-to-many relationship in the model. You do not have to change your database structure at all, it will be working perfectly as it is now.
See the documentation: ManyToManyField
You'll have to set the db_table option with the name of your intermediary table (i.e. students-subjects). Then everything should work fine.
EDIT:
Considering your comment, the problem is that Django expects a certain naming convention (i.e. MODELNAME_id) which isn't provided by your table. Since you say that you cannot change the table itself, you have to try something else.
You have to create an extra Model for your intermediary table (students-subjects) and define the field 'students' as a foreign key to the students model and the field 'subjects' as a foreign key to the subjects model. Then for the many-to-many field you specifiy the option 'through' with the name of your intermediary table. Set the options 'db_column' to let Django know which names you'd like to use for the databse columns. 'db_table' in the meta class is needed to specify your database table name.
You get something like:
class StudentsSubjects(models.Model):
student = models.ForeignKey(Student, db_column='student')
subject = models.ForeignKey(Subject, db_column='subject')
class Meta:
db_table = 'students-subjects'
class Student(models.Model):
...
subjects = models.ManyToManyField(Subject, through='StudentsSubjects')
...
class Subject(models.Model):
...
I hope that will help you.
For more detail see: Extra fields on many-to-many relationship.

Django model class that can either have one relationship or another?

To give you an idea of the problem I'm trying to solve I'll use an example. The issue is that there can be multiple possible relationships between classes, and how to represent this in the models file. In a shopping website the Department can either have a Sub-Department or a Category relationship. This can theoretically mean that one Department could have 100 sub departments until it has a category.
e.g. Department/Category/Item, Department/Department/Category/Category/Item, Department/Category/Category/Item...etc
My question is how best to describe this relationship in the Django models.py file? Would you just have two foreign keys and one would be empty?
I'd create a parent attribute on your Category and Department models, so that you can represent the hierarchical structure.
You can use a ForeignKey on the Department model to allow them to point to other Departments, and you can use a GenericKey on the Category model to allow it to point to Departments or other Categories. For example:
class Department(models.Model):
...
parent = models.ForeignKey('self', ...)
class Category(models.Model):
...
parent_content_type = models.ForeignKey(ContentType)
parent_id = models.PositiveIntegerField()
parent = generic.GenericForeignKey('parent_content_type', 'parent_id')
This will allow you to represent an arbitrary tree of categories under an arbitrary tree of departments.
You could use django tree implementations django-mptt or django-treebeard