jrief / django-formset

The missing widgets and form manipulation library for Django
https://django-formset.fly.dev/
MIT License
317 stars 30 forks source link

.DualSortableSelector incorrectly showing all available items as selected #153

Open jamiecash opened 1 month ago

jamiecash commented 1 month ago

When using the DualSortableSelector with a many-to-many relationship and a through table, the database is populated correctly, however the selector displays all available options on the right-hand side when there is more than one instance of the parent model.

Using the following model as an example: models.py

class Lesson(models.Model):
    name = models.CharField(max_length=100)

    class Meta:
      db_table = 'lessons_lesson'

class Course(models.Model):
   name = models.CharField(max_length=100)
   lessons = SortableManyToManyField(
      Lesson, 
      through="CourseLesson",
      verbose_name="lessons in the course")

   class Meta:
      db_table = 'lessons_course'

class CourseLesson(models.Model):
   course = models.ForeignKey(Course, on_delete=models.CASCADE)
   lesson = models.ForeignKey(Lesson,  on_delete=models.CASCADE)
   lesson_number = models.BigIntegerField(default=0)

   class Meta:
      ordering = ['lesson_number']

And the following form: forms.py

class CourseForm(forms.ModelForm):
    class Meta:
        model = models.Course
        fields = ['name', 'lessons']
        widgets = {
            'lessons': widgets.DualSortableSelector(),
        }

When you add a lesson to a course, this displays correctly. However, when you create a new course and add a lesson to it, both courses display both lessons.

When querying the database. The select statement returns the correct results. It appears that the widget is displaying all lessons in the through table, not just those assigned to the course.

I attempted to recreate using the test app however there are a few missing dependencies that I couldn't get to resolve, notably the .js files that do not appear to be included in static.

jamiecash commented 1 month ago

The following select statement

SELECT  c.name AS course,
        l.name AS lesson,
        cl.lesson_number
FROM    lessons_lesson l 
            INNER JOIN lessons_courselesson cl ON l.id = cl.lesson_id
            INNER JOIN lessons_course c ON cl.course_id = c.id
ORDER BY course, lesson_number

Returns one lesson assigned to each course. image

However when selecting the 'Computer Science' course, both lessons are displayed as selected.

image

jamiecash commented 1 month ago

I have isolated the cause of the issue down to the value_from_object method in SortableManyToManyField. The items returned in through._default_manager.select_related(field.name) contain all items, not just those selected.

Removing this method serves as a temporary workaround whilst I investigate further.

codematsing commented 1 month ago

I attempted to recreate using the test app however there are a few missing dependencies that I couldn't get to resolve, notably the .js files that do not appear to be included in static.

Hi! Just want to add in terms of this in case you have a similar problem as me, had to do the following so that the js file would appear due to missing npm dependency

npm install @tiptap/pm
npm run esbuild

Hope it helps!