Django: заполнение поля "многие ко многим" с помощью modelformset_factory

Я пытаюсь заполнить поле "многие-ко-многим". Отношение существует между меню и моделью курса. Я добавил несколько пользовательских полей в таблицу M2M, чтобы хранить порядок курсов и их тип (т.е. Starter, Appetizer и т.д.).

Для получения динамической веб-формы я использую modelformset_factory с пакетом python-formset-js-improved pip (https://pypi.org/project/django-formset-js-improved/).

следуя логике в моем views.py, объясненной здесь Populate a ManyToManyField, я столкнулся со следующей ошибкой. Эта ошибка относится ко всем аргументам ключевых слов.

menu.menu_item.add(course_position=position,
                                   course_type=menu_item_form['course_type'],
                                   course=course)

TypeError: add() got an unexpected keyword argument 'course_position'

Что я сделал не так? Ниже приведены выдержки из models.py, forms.py, views.py и html-файла

models.py

forms.py

class MenuItemForm(forms.ModelForm):
    course_type = forms.ChoiceField(choices=MenuItem.CourseType.choices)

    class Meta:
        model = MenuItem
        fields = '__all__'


MenuItemFormset = modelformset_factory(MenuItem,
                                       form=MenuItemForm,
                                       extra=1)

class CourseForm(forms.ModelForm):

    class Meta:
        model = Course
        fields = ['course_name', 'course_description']

    class Media(object):
        # todo: can this be deleted? used for
        js = formset_media_js + (
            # Other form media here
        )


CourseFormset = modelformset_factory(Course,
                                     form=CourseForm,
                                     extra=1)

views.py

def create_menu_with_courses(request):
    context = {}
    user = request.user

    menu_form = MenuCreationForm(user, request.POST or None)
    course_formset = CourseFormset(request.POST or None, queryset=Course.objects.none(), prefix='course')

    menu_item_formset = MenuItemFormset(request.POST or None, queryset=MenuItem.objects.none(), prefix='menu-item')

    print(f"debug: {request.POST}")

    if request.method == 'POST':
        current_user = request.user

        if all([menu_form.is_valid(), course_formset.is_valid()]):
            print("menu & course_formset is valid")

            menu = menu_form.save(commit=False)
            menu.creator = current_user
            menu.save()

            for (position, course_form), menu_item_form in zip(enumerate(course_formset), menu_item_formset):
                course = course_form.save(commit=False)
                course.creator = request.user
                course.save()

                menu.menu_item.add(course_position=position,
                                   course_type=menu_item_form.course_type,
                                   course=course)

                menu.save()

            messages.success(request, f'Well done! Your menu "{menu}" was successfully created!')

            return redirect('menu-list')

        else:
            print(menu_form.errors, course_formset.errors)

    context['menu_form'] = menu_form
    context['course_formset'] = course_formset
    context['menu_item_formset'] = menu_item_formset

    return render(request, 'menus/create_menu.html', context)

html

Почему вы получили сообщение об ошибке, объясняется просто. Строка menu.menu_item.add(...) ожидает Course, как вы определили в menu_item = models.ManyToManyField(Course, through='MenuItem', blank=True), но вы также вводите другие параметры, например course_position=position. Эти поля соответствуют вашему классу MenuItem. Остаются трудные вопросы: как вы получаете переменную course. И почему вы пытаетесь фильтровать добавляемый курс, добавляя дополнительные параметры?

Основываясь на этой теме (Установите значение поля формы перед is_valid()) я решил свою проблему.

Я добавил поле course_type в course_formset. Я извлек данные из request.POST.

menu_item = MenuItem(menu=menu, course=course, course_position=position, course_type=request.POST[f'course-{position}-course_type'])
menu_item.save()
Вернуться на верх