我尝试填充一个多对多字段。菜单和课程模型之间存在关系。我在M2M Table中添加了一些自定义字段,这样我就可以存储课程的顺序及其类型(即Starter、Appetizer等)。
为了获得一个动态的webform,我使用modelformset_factory和python-formset-js改进的pip包(https://pypi.org/project/django-formset-js-improved/)。
遵循我的views.py中的逻辑,在这里解释填充一个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文件
的摘录编辑我遵循文档并尝试相应地填充m2m表。https://docs.djangoproject.com/en/4.0/topics/db/models/intermediary-manytomany
我试图将值添加到模型使用,但我不知道如何访问menu_item_form的course_type字段,因为我无法验证表单:
menu_item = MenuItem(menu=menu,
course=course,
course_position=position,
course_type=menu_item_form.fields['course_type'])
menu_item.save()
models.py
class Course(models.Model):
# Individual name of a course (i.e. "Natschis Spezial Fondue")
creator = models.ForeignKey(User, related_name='creator_id_course', on_delete=models.PROTECT)
course_name = models.CharField(max_length=100)
course_description = models.CharField(max_length=1000)
course_price = models.DecimalField(max_digits=7, decimal_places=2, null=True)
course_tags = models.ManyToManyField(CourseTag)
private = models.BooleanField(default=False, verbose_name=_('private item'))
active = models.BooleanField(default=True)
deleted = models.BooleanField(default=False)
objects = CourseManager()
def __str__(self):
return self.course_name
class Menu(models.Model):
class Status(models.TextChoices):
ACTIVE = 'a', _('active')
SUSPENDED = 's', _('suspended')
DELETED = 'd', _('deleted')
# assemble a menu from different courses --> "Movie"
creator = models.ForeignKey(User, related_name='creator_id_menu', on_delete=models.PROTECT)
menu_name = models.CharField(max_length=100, default='', verbose_name=_('Menu Name'), help_text=_('i.e. saturday night fajita night'))
menu_description = models.TextField(max_length=1000, default='', verbose_name='Menu Description', help_text=_('this menu will blow your mind...'))
menu_duration_minutes = models.IntegerField(default=30, blank=True, null=True)
menu_item = models.ManyToManyField(Course, through='MenuItem', blank=True) # collects all courses related to this menu
created_at = models.DateTimeField(auto_now_add=True, verbose_name='Creation Date')
updated_at = models.DateTimeField(auto_now=True, verbose_name='Updated on')
private = models.BooleanField(default=False, verbose_name=_('private'))
status = models.CharField(max_length=1, default='a', choices=Status.choices, verbose_name=_('menu status'))
objects = MenuManager()
def __str__(self):
return self.menu_name
class MenuItem(models.Model):
class CourseType(models.TextChoices):
APPETIZER = '10', _('appetizer')
STARTER = '20', _('starter')
MAIN = '30', _('main course')
DESSERT = '40', _('dessert')
SOUP = '21', _('soup')
SALAD = '22', _('salad')
PRIMO = '31', _('primo')
PASTA = '32', _('pasta')
SECONDO = '33', _('secondo')
FISH = '34', _('fish')
MEAT = '35', _('meat')
PIZZA = '36', _('pizza')
HAMBURGER = '37', _('hamburger')
CHEESE = '41', _('cheese')
FRUITS = '42', _('fruits')
CAKES = '43', _('cakes')
ICE_CREAM = '44', _('ice cream')
DIGESTIVE = '45', _('digestive')
menu = models.ForeignKey(Menu, on_delete=models.CASCADE, verbose_name=_('menu name'))
course = models.ForeignKey(Course, on_delete=models.PROTECT, verbose_name=_('course name'))
course_type = models.CharField(choices=CourseType.choices, max_length=3, verbose_name=_('course type'))
course_position = models.PositiveSmallIntegerField(verbose_name=_('course position'))
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)