djangodjango-modelsmany-to-manymanytomanyfield

Access values of manyToMany field before saving


This is my Classroom model:

class Classroom(models.Model):
    name = models.CharField(max_length=120)
    faculty = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='faculty')
    students = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='students')

    def save(self, *args, **kwargs):
        for faculty in self.faculty.all():
            if not faculty.user_type == 'faculty':
                raise ValidationError('Only users with user type as \'faculty\' can be assigned as faculty to a classroom')
        
        for student in self.students.all():
            if not student.user_type == 'student':
                raise ValidationError('Only users with user type as \'student\' can be assigned as students to a classroom')
        
        return super(Classroom, self).save(*args, **kwargs)

    def __str__(self):
        return self.name

The user model has a property called user_type which is either faculty or student. In the Classroom model I want to make sure that only users with user_type as faculty can be selected as faculty and similarly for students.

On trying to add data using django admin, I receive the following error:

ValueError at /admin/classroom/classroom/add/
"<Classroom: Some Class>" needs to have a value for field "id" before this many-to-many relationship can be used.

Not sure how to fix it, kindly guide. Thanks in advance.


Solution

  • After hours of research, I have found out a solution that works. I created a new Django Form for creating a Classroom and overrode the clean method and it works.

    class ClassroomForm(forms.ModelForm):
        class Meta:
            model = Classroom
            fields = ['name', 'faculty', 'students']
            required = ['name', 'faculty', 'students']
    
        def clean(self) -> dict[str, Any]:
            if 'faculty' in self.cleaned_data.keys():
                for faculty in self.cleaned_data['faculty']:
                    if not faculty.user_type == 'faculty':
                        raise forms.ValidationError(f"Only users with user type as 'faculty' can be assigned as faculty to a classroom. {faculty.email} is not a faculty.")
    
            if 'students' in self.cleaned_data.keys():
                for student in self.cleaned_data['students']:
                    if not student.user_type == 'student':
                        raise forms.ValidationError(f"Only users with user type as 'student' can be assigned as students to a classroom. {student.email} is not a student.")
                
            return super().clean()
    

    Leaving it here so that it might help someone else.