Hi there,
Suppose I two models which have ManyToMany relation with each other. And suppose my models are below"
class Student(models.Model):
name = models.CharField(max_length=254)
courses = models.ManyToManyField(Course, required=False)
finished = models.BooleanField(default=False) # THIS KIND OF FUNCTIONALITY I WANT
class Course(models.Model):
name = models.CharField(max_length=254)
Now what I explained above might seem a right solution, but look at this example:
john = Student.objects.create(name="John")
eng_course = Course.objects.create(name="English")
math_course = Course.objects.create(name="Mathematics")
cs_course = Course.objects.create(name="Computer Science")
john.courses.add(eng_course)
john.courses.add(math_course)
john.courses.add(cs_course)
john.save()
Now lets say john
has finished eng_course
, and I want the finished
for eng_course
to be True
. So, if I do below:
john.finished = True
john.save()
Then this will make finished
have True
for all courses, but I just want it to have True
for eng_course
only.
So, I hope I tried my best to explain the problem. So, can anyone help me in implementing the functionality/behavior I am desiring?
Hey there!
For what you issued it looks like you want to specify custom attributes on a Many to Many relationship.
@leandrodesouzadev I read the link that you shared but I did not understand it. Can you provide me an example on the code that I shared in my question? Also let’s say we have intermediary Model Finished
, how would I access the intermediary Model’s attributes?
Like if I want to see the finished
status of john
on eng_course
, should I do it like this:
john_eng_course_finished = Finished.objects.create(student=john, course=eng_course, finished=False)
john.courses.filter(name="English").finished
or if I want to update the same, then should I do it like this:
john.courses.filter(name="English").finished = True
john.save()
eng_course.save()
john_eng_course_finished.save()
Kindly clear up all the things I stated above and if possible please explain me providing the example using the Models I wrote in my question.
When you have a Student
and a Course
, students Enroll
on that course.
You can add a Enrollment
table that would look like
class Enrollment(models.Model):
student = models.ForeignKey("Student", on_delete=models.CASCADE)
course = models.ForeignKey(Course, on_delete=models.CASCADE)
finished = models.BooleanField(default=False)
class Student(models.Model):
# other infos
enrollments = models.ManyToManyField(Course, through=Enrollment)
1 Like
Then you can add your enrollments:
Enrollment.objects.create(course=course, student=student)
Or maybe using the enrollments set on the student.
student.enrollments.create(through_defaults={"course": course})
1 Like
That’s not necessary. Using the through
attribute still allows all the normal many-to-many operations to be performed. You can still use operations like john.courses.add(eng_course)
. When you don’t need to reference the through
table, you can ignore it.
(I would also recommend keeping the name courses
as the reference to Course
.)
1 Like
@leandrodesouzadev and @KenWhitesell thank you for explaining. I have one question which is still not answered. And that is how would I access the finished
value for john
on eng_course
? Secondly, also tell me if we have to make the class Diagram or Database Design, then what relationship should be should between the two classes Student
& Course
and the other class Enrollment
?
Ignore the many-to-many relationship for the moment.
Look at just the Student and Enrollment models.
What you have is a reverse foreign key relationship from Student to Enrollment.
If john
is your Student
, then john.enrollment_set
is the related object manager created for that relationship, and john.enrollment_set.all()
is the complete set of all Enrollment
for john
.
In this case, since you only want one instance of Enrollment
, you can use get:
john.enrollment_set.get(course=eng_course)
(You do want to ensure that in the Enrollment model, the fields student
and course
are defined as unique together.)
In all cases, a many-to-many relationship is modeled using a “join table” connecting two tables. This join table consists of two foreign keys, one to each of the two tables it is joining. This is a standard relational db technique.
The differences between using the through
attribute and not using it is that by using it, you’re making that join table to be an explicit model and not something implicitly created by Django and otherwise hidden from view.
So, depending upon how your instructor would want to see it modelled, you can continue to show this as just a many-to-many relationship between the two with an annotation describing the additional fields.
Or, if you want to show the additional detail of that Enrollment model, the relationship is the same as any other ForeignKey relationship. You have a Many-To-One relationship from Enrollment to both Student and Course. (The “many” side of a many-to-one relationship is always the table with the Foreign Key.)
2 Likes
Thank you @KenWhitesell for explaining in detail.