10

I am trying to build a website that users can add the courses they are taking. I want to know how should I add the ManyToMany relationship. Such that we can get all users in a course based on the course code or instructor or any field. And we can also get the courses user is enrolled in. Currently, my Database structure is:

class Course(models.Model):
    course_code = models.CharField(max_length=20)
    course_university = models.CharField(max_length=100)
    course_instructor = models.CharField(max_length=100)
    course_year = models.IntegerField(('year'), validators=[MinValueValidator(1984), max_value_current_year])

    def __str__(self):
        return self.course_code

and my user model:

class Profile(AbstractUser):
    bio = models.TextField()
    image = models.ImageField(default='defaults/user/default_u_i.png', 
    courses = models.ManyToManyField('home.Course',related_name='courses')

    def __str__(self):
        return self.username

I was wondering should ManyToMany relationship be in User model or the course model? Or will it make any difference at all?

EDIT: For adding course to post object now I am using this view but it seems to not work:

@login_required
def course_add(request):

    if request.method == "POST":
        form = CourseForm(request.POST or none)
        if form.is_valid():
            course = form.save()
            request.user.add(course)
    else:
        form = CourseForm
    context = {
        'form':form
    }
    return render(request,'home/courses/course_add.html', context)
ashes999
  • 1,234
  • 1
  • 12
  • 36
  • 1
    It will not make much difference. Django will create an extra table with two foreign keys to the models. The rest is just Django adding related managers to the models and making ORM queries more convenient. – Willem Van Onsem May 02 '20 at 21:56
  • So my current models is correct? I can do both retrieving user courses and users in a course? – ashes999 May 02 '20 at 22:00
  • 1
    well the `related_name` needs to be changed, to `profiles` for example. – Willem Van Onsem May 02 '20 at 22:01
  • linking [this question](https://stackoverflow.com/a/47205190/6359659) here. They are related and solved my problem as a couple. – awadhesh14 Apr 18 '22 at 13:16

2 Answers2

15

For a relational databases, the model where you define the ManyToManyField does not matter. Django will create an extra table with two ForeignKeys to the two models that are linked by the ManyToManyField.

The related managers that are added, etc. is all Django logic. Behind the curtains, it will query the table in the middle.

You however need to fix the related_name=… parameter [Django-doc]. The related_name specifies the name of the relation in reverse so from Course to Profile in this case. It thus should be something like 'profiles':

class Profile(AbstractUser):
    bio = models.TextField()
    image = models.ImageField(default='defaults/user/default_u_i.png', 
    courses = models.ManyToManyField('home.Course', related_name='profiles')

    def __str__(self):
        return self.username

You thus can obtain the people that particiate in a Course object with:

mycourse.profiles.all()

and you can access the courses in which a Profile is enrolled with:

myprofile.courses.all()

For more information, see the Many-to-many relationships section of the documentation.

You can add a course to the courses of a user with:

@login_required
def course_add(request):
    if request.method == 'POST':
        form = CourseForm(request.POST)
        if form.is_valid():
            course = form.save()
            request.user.courses.add(course)
    else:
        form = CourseForm()
    context = {
        'form': form
    }
    return render(request,'home/courses/course_add.html', context)
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • I also now face an issue how can I add a course object to a user I have written a view for it im not sure if it's implemented correctly. – ashes999 May 02 '20 at 22:33
  • 1
    You can add a course with `myprofile.courses.add(mycourse)`. `myprofile` and `mycourse` first need to be added to the database. This is explained in the documentation: https://docs.djangoproject.com/en/3.0/topics/db/examples/many_to_many/ – Willem Van Onsem May 02 '20 at 22:34
  • Is it somewhere documented that an extra table will be created? I have seen it in my own project but I didn't saw it inside the documentation. – Aalexander Aug 23 '21 at 14:28
  • @Aalexander: yes, see the [*Database Representation* section](https://docs.djangoproject.com/en/3.2/ref/models/fields/#id1) of the `ManyToManyField`. – Willem Van Onsem Aug 23 '21 at 16:52
0

You don't need to add the related name. Default is "courses_set" in your case. Here is excerpt from: https://docs.djangoproject.com/en/dev/topics/db/queries/#backwards-related-objects

Following relationships “backward” If a model has a ForeignKey, instances of the foreign-key model will have access to a Manager that returns all instances of the first model. By default, this Manager is named FOO_set, where FOO is the source model name, lowercased. This Manager returns QuerySets, which can be filtered and manipulated as described in the “Retrieving objects” section above.

Sofía
  • 784
  • 10
  • 24