Home > Enterprise >  Edit Many To Many Field in Django
Edit Many To Many Field in Django

Time:02-16

I am having a problem in editing a page in my code. Basically I have a page where I have multiple select field where I can select the students. But I have a problem in understanding how to remove a specific student, if I need to edit this page. Let me be more clear with some code.

models.py

class TheorySyllabus(models.Model):
    name = models.CharField(max_length=100, null=True, blank=True)
    subject_duration = models.ManyToManyField(
        SubjectDuration, related_name='theory_syllabus')
    course_type = models.ForeignKey(
        CourseType, on_delete=models.DO_NOTHING, null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = 'Theory Syllabus'


class TheoryCourse(models.Model):
    name = models.CharField(max_length=100)
    student = models.ManyToManyField(Student, related_name='theory_courses')
    theory_syllabus = models.ForeignKey(
        TheorySyllabus, on_delete=models.DO_NOTHING, null=True, blank=True)
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name

views.py

def edit_theory_course(request, pk):
    theory_course = TheoryCourse.objects.get(id=pk)
    student_obj = theory_course.student.all()
    theory_syllabus = TheorySyllabus.objects.all()
    students = Student.objects.filter(is_active=True).order_by('last_name')
    context = {
        'theory_course': theory_course,
        'theory_syllabus': theory_syllabus,
        'students': students,
        'student_obj': student_obj,
    }
    if request.method == 'POST':
        course_name = request.POST.get('course_name')
        student = request.POST.getlist('student')
        syllabus = request.POST.get('syllabus')
        try:
            theory_course.name = course_name
            theory_course.theory_syllabus_id = syllabus
            theory_course.save()
            for stud in student:
                theory_course.student.add(stud)

            theory_course.save()

            messages.success(request, "Theoretical Course Edited")
            return HttpResponseRedirect(reverse('theory:course_list'))
        except:
            messages.error(request, "Failed to Edit Theoretical Course")
            return HttpResponseRedirect(reverse('theory:edit_theory_course', kwargs={'pk': pk}))
    return render(request, 'theory/edit_theory_course.html', context)

I know that basically, what I need to do is in the view to place an if statement to compare the two lists and remove (if needed) the value that is not part of the entry list anymore. The problem is that I have no clue how to place this logic. Any help is highly appreciated. Thank you very much

CodePudding user response:

I think you can replace the full list of students at once by doing theory_course.student.set([list_of_student_pks])

CodePudding user response:

A very clean and easy way to accomplish this is to make a query for all of the current students (which you don't even need to do since it's already in theory_course), then just simply get the difference between the students in that query and the submitted students. This will yield you the students that were removed, which you can then iterate over and delete.

current_students = theory_course.students
selected_students = Student.objects.filter(pk__in=student)
removed_students = current_students.difference(selected_students)

for s in removed_students:
    s.delete()

There is however an option to set all of the IDs at once using and Object's set() method, but having the list of removed users so that you can make necessary changes (such as sending emails) is much better from my experience.

  • Related