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()