GTEE
GTEE

Reputation: 11

Add to m2m field on seperate model

Problem:

I have 3 models in 3 separate apps

School
Student
Classroom

class School(models.Model): 
    Name = models.CharField(max_length=100)
    Students = models.ManyToManyField(Student) 


class Classroom(models.Model): 
    School = models.ForeignKey(School, on_delete=models.CASCADE)
    Students = models.ManyToManyField(Student) 

What I want:

When a new classroom is saved the students that are in that classroom instance are automatically added to the students many-to-many field of the school model.

Then in the school page template I want to display the students in order of the date they were added to the school model, I think this part needs a separate through model but just can't get my head around it.

Upvotes: 0

Views: 43

Answers (2)

Ahmed I. Elsayed
Ahmed I. Elsayed

Reputation: 2130

First, I'd like to stick with some pep8 fixes

class School(models.Model): 
    name = models.CharField(max_length=100)
    students = models.ManyToManyField(Student) 

class Classroom(models.Model): 
    school = models.ForeignKey(School, on_delete=models.CASCADE)
    students = models.ManyToManyField(Student) 

Second, unless you've a reason to set name.max_length to 100, set it to 255.

    name = models.CharField(max_length=255)

I'd also set a related name to make things easier

class Classroom(models.Model): 
    school = models.ForeignKey(School, on_delete=models.CASCADE, related_name='classrooms')
    students = models.ManyToManyField(Student) 

Now, to your problem.

Your logic is saying that a school can have many students and a student can enroll in many schools (weird but I'll cope), a classroom can exist inside a school and a school can have multiple classrooms, a classroom HAS MANY STUDENTS. This is a key here, you don't need to save the students in the school itself, you can just use something like

class School(models.Model): 
    name = models.CharField(max_length=100)
    students = models.ManyToManyField(Student) 
    
    @property
    def students(self):
        # iterate the class room, return a QS with all students from them
        # there're operators for union, but I'm using the function itself
        qs = Student.objects.none()
        for classroom in self.classrooms: # related name in action
            qs = qs.union(classroom.students)

        return qs

I'll add a note, the behavior you want can be done in a signal # DON'T DO THING, JUST KNOW IT

More info

Upvotes: 1

Arman Ordookhani
Arman Ordookhani

Reputation: 6556

When a new classroom is saved the students that are in that classroom instance are automatically added to the students many-to-many field of the school model.

There's no such behavior, I can suggest this:

Remove School.Students, then any time you need students of a specific school, you do something along the lines

Students.objects.filter(classroom__School__Name="Hogwarts")

If you want to keep both (for whatever reason) you can create your custom functions that add items into both. Or you can create custom managers to modify default behavior.

Then in the school page template I want to display the students in order of the date they were added to the school model - I think this part needs a separate through model but just can't get my head around it.

You're right, if you want to add any field to a many-to-many you need to define your custom model. Then you can create a many-to-many out of it with through option.

Upvotes: 1

Related Questions