M2M 字段小部件将数据添加或更新为内联表单集

M2M field widget to add or update data as inline formset

我花了很多时间寻找一个我认为应该是 Django 中非常基本的功能的特性。但我无法让它工作,

我找不到与 django 的 m2m 小部件功能相同的小部件,但如果它不存在,也会创建新的模型实例。

注意:这里的模型实例已经存在意味着输入inline widget的数据已经存在于数据库中。

例如 如果我有以下模型:

class Outcome(models.Model):
    outcome = models.CharField(max_length=255)
    outcome_short_name = models.CharField(max_length=10, blank=True, null=True)


class Course(models.Model):
    course_title = models.CharField(
        verbose_name=COURSE_SINGULAR + " title", max_length=200, unique=True
    )

    course_outcome = models.ManyToManyField(
        Outcome, verbose_name=COURSE_SINGULAR + " outcome", blank=True
    )

然后我希望 "Outcomes" 在创建课程时显示为此图像: Image of adding new course with inline outcomes

现在,如果用户添加的结果数据已经存在,那么它应该只将它们映射到课程。否则它应该首先将结果存储到数据库中,然后将它们映射到课程。

任何正确方向的指导将不胜感激。

谢谢,

编辑: 正如@dirkgroten 建议使用 modelformset,我将 FormView 更改为:

class CourseFormView(FormView):
    template_name = "course/course_form.html"
    form_class = CourseForm
    success_url = "/admin/"

    def get_context_data(self, **kwargs):
        context = super(CourseFormView, self).get_context_data(**kwargs)
        if self.request.POST:
            context["outcomes"] = OutcomeFormSet(self.request.POST)
        else:
            context["outcomes"] = OutcomeFormSet(queryset=Outcome.objects.none())
        return context

    def form_valid(self, form, **kwargs):
        super(CourseFormView, self).get_context_data(**kwargs)
        context = self.get_context_data()
        outcomes_formset = context["outcomes"]
        if not outcomes_formset.is_valid():
            return super().form_invalid(form)

        cleaned_data = form.cleaned_data
        cleaned_data.pop("course_outcome")
        course = Course.objects.create(**cleaned_data)
        course.save()

        outcomes_formset.instance = course
        outcomes_formset.save()


course.course_outcome.set(Outcome.objects.filter(course_outcome=course))
    return super().form_valid(form)

一切看起来都很好,除了我的 model_formset 如果表单集中的表单数据已经存在于数据库中,则未验证。
例如。如果我在表单集中输入 (outcome="test_outcome", outcome_short_name="test_short") 并且结果 table 中已经存在相同的数据,那么我的表单集会给出错误:
具有此 Outcome 和 Outcome 简称的结果已经存在。

有什么办法可以解决这种情况,或者我做错了什么。

您可以在上面进行测试:http://code.gdy.club:8001/course/add/
outcomes_list: http://code.gdy.club:8001/outcome/

谢谢,

--
苏拉吉
https://hacksj4u.wordpress.com
https://github.com/SurajDadral

您需要自己处理 Outcome 已经存在的情况。验证表单时的默认设置是假定将创建一个新对象,因此如果您的字段设置为 unique_together,则单个表单将不会验证。

您可以在 OutcomeFormsetclean() 方法上这样做:

from django.core.exceptions import NON_FIELD_ERRORS

def clean(self):
    super().clean()
    for i in range(0, self.total_form_count()):
        form = self.forms[i]
        if form.non_field_errors() and len(form.errors) == 1:
            # the only error is probably due to unique_together constraint
            try:
                form.instance = Outcome.objects.get(
                    outcome=form.data.get(form.add_prefix('outcome')),
                    outcome_short_name=form.data.get(form.add_prefix('outcome_short_name')))
            except Outcome.DoesNotExist:
                pass  # some other error so we should keep it
            else:
                del form._errors[NON_FIELD_ERRORS]

然后在您看来,在保存表单集时,您应该遍历所有表单,而不是保存实例具有 pk:

的表单
for form in outcomes_formset:
    outcome = form.instance    
    if not outcome.pk:
        outcome = form.save()
    course.course_outcome.add(outcome)