0

I have the following django model

class Course(models.Model):
   name = models.CharField(max_length=50)
   pre_req_courses = models.ManyToManyField('self')

   def __str__(self):
      return self.name

when I create courses in following way:

course1 = Course.objects.create(name='Course1')
course1.save()

course2 = Course.objects.create(name='Course2')
course2.save()
course2.pre_req_courses.set(course1)

when I run the following command I get:

course2.pre_req_courses.all()
>>> <QuerySet [<Course: Course1>]>

course1.pre_req_courses.all()
>>> <QuerySet [<Course: Course2>]>

Wht I want is:

course2.pre_req_courses.all()
>>> <QuerySet [<Course: Course1>]>

course1.pre_req_courses.all()
>>> <QuerySet []>

How can I achieve this

Ahzam Ahmed
  • 11
  • 1
  • 2
  • 1
    Have you tried setting `related_name`? – NixonSparrow Sep 21 '22 at 11:59
  • 1
    Does this answer your question? [Django ManyToMany relation to 'self' without backward relations](https://stackoverflow.com/questions/19837728/django-manytomany-relation-to-self-without-backward-relations) – Abdul Aziz Barkat Sep 21 '22 at 14:55
  • This worked for me Thank you @AbdulAzizBarkat!! I searched through StackOverflow before posting this question but didn't find this. – Ahzam Ahmed Sep 21 '22 at 20:43

1 Answers1

0

I couldn't solve it with the m2m field.

It's a little complicated, but you can get the result you want with the code below.

models.py

from django.db import models


class CourseRelation(models.Model):
    low_level = models.ForeignKey(
        'course.Course',
        models.CASCADE,
        related_name='relations_me_low'
    )
    high_level = models.ForeignKey(
        'course.Course',
        models.CASCADE,
        related_name='relations_me_high'
    )

    def __str__(self):
        return '%s -> %s' % (self.low_level, self.high_level)


class Course(models.Model):
    name = models.CharField(max_length=50)

    def __str__(self):
        return self.name

    @property
    def pre_req_courses(self):
        course_ids = CourseRelation.objects.filter(high_level=self).values('low_level')
        return Course.objects.filter(id__in=course_ids)

    @property
    def next_courses(self):
        course_ids = CourseRelation.objects.filter(low_level=self).values('high_level')
        return Course.objects.filter(id__in=course_ids)

shell

>>> python_course = Course.objects.create(name='Python')
>>> web_course = Course.objects.create(name='Web')
>>> django_course = Course.objects.create(name='Django')

>>> CourseRelation.objects.create(low_level=python_course, high_level=django_course)
<CourseRelation: Python -> Django>

>>> CourseRelation.objects.create(low_level=web_course, high_level=django_course)
<CourseRelation: Web -> Django>

>>> python_course.pre_req_courses
<QuerySet []>

>>> python_course.next_courses
<QuerySet [<Course: Django>]>

>>> django_course.pre_req_courses
<QuerySet [<Course: Python>, <Course: Web>]>

>>> django_course.next_courses
<QuerySet []>

update

When I read Abdul Aziz Barkat's comment, I realized that a single line would solve it.

pre_req_courses = models.ManyToManyField(
    'self',
    related_name='next_courses',
    symmetrical=False
)
gypark
  • 251
  • 1
  • 8
  • 1
    This can totally be solved with many to many fields, see the [documentation](https://docs.djangoproject.com/en/4.1/ref/models/fields/#django.db.models.ManyToManyField.symmetrical) – Abdul Aziz Barkat Sep 21 '22 at 14:58
  • omg. I wasted my time, but it's a parameter that I'll never forget from now. – gypark Sep 21 '22 at 15:08