【问题标题】:Adding data after submitting form in Django admin在 Django admin 中提交表单后添加数据
【发布时间】:2018-11-03 20:57:28
【问题描述】:

(我对 Django 很陌生,并且使用 Django 1.8 的代码库)

有一个模型 (Survey) 与另一个模型 (Page) 有多对多的关系,并且对起始页面 (Page) 的引用。在管理面板中创建这个对用户来说真的很麻烦,但如果他们做出一些是/否的决定,就可以解决。

因此,在管理添加页面中,我添加了一些不属于模型的选择字段(或多或少“包括 A 部分?”/“包括 B 部分?”/“包括 C 部分?”...等),并根据用户在此处选择的内容,我想向调查页和起始页上的 page_links 添加不同的键。

解决这些问题很好,但是当保存表单/模型时,page_links 和 start_page 不存在,大概是因为它们不是表单的一部分。

我尝试将它们添加到cleaned_data(保存或清理),但这不起作用。

这是模型:

class Survey(models.Model):
    title = models.CharField(max_length=255)
    start_page = models.ForeignKey('Page', related_name='surveys_started', null=True)
    page_links = models.ManyToManyField('PageLink', related_name='surveys')

这里是管理员:

class SurveyAdmin(admin.ModelAdmin):

    fieldsets = (
        (None, {
            'fields': ('title', ),
        }),
        ('User Decisions', {
            'fields': ('do_a', 'do_b', 'do_c', )
        }),
    )

    form = SurveyForm

和表格:

class SurveyForm(forms.ModelForm):

    title = forms.CharField(required=True)
    do_a = forms.ChoiceField(label='Do A',choices=[(True, 'Yes'), (False, 'No')], initial=True)
    do_b = forms.ChoiceField(label='Do B',choices=[(True, 'Yes'), (False, 'No')], initial=True)
    do_c = forms.ChoiceField(label='Do C',choices=[(True, 'Yes'), (False, 'No')], initial=True)

    def save(self, commit=True):
        title = self.cleaned_data.get('title', 'N/A')
        do_a = self.cleaned_data.get('do_a', True)
        do_b = self.cleaned_data.get('do_b', True)
        do_c = self.cleaned_data.get('do_c', True)

        # Assume these two work fine
        page_links = work_out_links(do_a, do_b, do_c)
        start_page = get_start_page(do_a, do_b, do_c)

        self.cleaned_data['page_links'] = page_links
        self.cleaned_data['start_page'] = start_page
        return super(SurveyForm, self).save(commit=commit)

    class Meta:
        exclude = ['start_page']

基本上我只希望 save_model 中的“page_links”和“start_page”的内容在数据库中的 Survey 实例中。

(注意:我知道我似乎应该创建一个中间“部分”并让用户选择那些,但假设如果可能的话我不这样做是有原因的 8)

非常感谢任何帮助。

【问题讨论】:

  • ModelForm 在 1.8 中是否有一个名为 save_model 的方法?不就是save吗?
  • @CoffeeBasedLifeform 是的,这是一个错字,刚刚编辑了,谢谢。

标签: python django django-forms django-admin


【解决方案1】:

好的,结果证明一个可行的方法是正常返回表单数据,然后在 admin.ModelAdmin 的 save_model 方法中进行分配。

所以表格变成了:

class SurveyForm(forms.ModelForm):

    title = forms.CharField(required=True)
    do_a = forms.ChoiceField(label='Do A',choices=[(True, 'Yes'), (False, 'No')], initial=True)
    do_b = forms.ChoiceField(label='Do B',choices=[(True, 'Yes'), (False, 'No')], initial=True)
    do_c = forms.ChoiceField(label='Do C',choices=[(True, 'Yes'), (False, 'No')], initial=True)

    def save(self, commit=True):
        title = self.cleaned_data.get('title', 'N/A')
        do_a = self.cleaned_data.get('do_a', True)
        do_b = self.cleaned_data.get('do_b', True)
        do_c = self.cleaned_data.get('do_c', True)

        # Assume these two work fine
        page_links = work_out_links(do_a, do_b, do_c)
        start_page = get_start_page(do_a, do_b, do_c)

        self.cleaned_data['page_links'] = page_links
        self.cleaned_data['start_page'] = start_page
        return super(SurveyForm, self).save(commit=commit)

    class Meta:
        exclude = ['start_page']

ModelAdmin 变成了:

class SurveyAdmin(admin.ModelAdmin):

    fieldsets = (
        (None, {
            'fields': ('title', ),
        }),
        ('User Decisions', {
            'fields': ('do_a', 'do_b', 'do_c', )
        }),
    )

    form = SurveyForm

    def save_model(self, request, obj, form, change):
        obj.start_page = form.cleaned_data['start_page']
        obj.save()
        # Need to save _before_ adding foreign keys
        obj.page_links = form.cleaned_data['page_links']

这很好用,(编辑)虽然我几乎肯定应该将更多逻辑从 form.save 移到 modelAdmin.save_model。

【讨论】:

    【解决方案2】:

    问题是ModelForm.save() 正在保存的实例是在ModelForm._post_clean 的清理过程中较早构建的:

    def _post_clean(self):
        ...
        try:
            self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude)
        except ValidationError as e:
            self._update_errors(e)
    
        try:
            self.instance.full_clean(exclude=exclude, validate_unique=False)
        except ValidationError as e:
            self._update_errors(e)
        ...
    
    def save(self, commit=True):
        ...
        if commit:
            # If committing, save the instance and the m2m data immediately.
            self.instance.save()
            self._save_m2m()
        else:
            # If not committing, add a method to the form to allow deferred
            # saving of m2m data.
            self.save_m2m = self._save_m2m
        return self.instance
    

    因此,ModelForm.save() 实际上根本没有使用cleaned_data,这意味着在该方法中更改cleaned_data 的内容没有效果。

    您自己提供的答案有效,因为您使用ModelForm.save() 方法将数据添加到form.cleaned_data,然后在ModelAdmin.save_model 中使用。
    从技术上讲,这很好,因为从 ModelForm 的角度来看,更改 cleaned_data 不会影响实例的保存/构造方式。

    更简洁的方法是将clean 方法添加到添加您需要的数据的 ModelForm 中。 clean() 发生在 _post_clean() 之前,因此您对 cleaned_data 所做的任何更改都将包含在构造实例时(以及保存实例时)。

    def clean(self):
        self.cleaned_data['page_links'] = work_out_links(do_a, do_b, do_c)
        self.cleaned_data['start_page'] = get_start_page(do_a, do_b, do_c)
        return self.cleaned_data
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-04-09
      • 1970-01-01
      • 2023-04-08
      • 2015-07-11
      • 1970-01-01
      • 2022-01-05
      • 1970-01-01
      • 2015-07-01
      相关资源
      最近更新 更多