在Django管理员中提交表单后添加数据

时间:2018-11-03 20:57:28

标签: python django django-forms django-admin

(我刚接触Django,并在Django 1.8中使用了代码库)

有一个模型(调查),该模型与另一个模型(页面)具有多对多关系,并具有对起始页面(页面)的引用。在管理面板中创建该控件对于用户来说确实很麻烦,但是如果他们做出一些是/否的决定,则可以解决。

因此,在“管理员添加”页面中,我添加了一些不属于模型的选择字段(或多或少“包含A部分?” /“包含B部分?” /“包含C部分?” ...等),并根据用户在此处选择的内容,向“勘测”和“ starting_page”上的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实例中。

(注意:据我所知,我似乎应该创建一个中间的“ Section”,然后让用户选择那些“ Section”,但要假设有可能的话,我可能不这样做8))

非常感谢任何帮助。

2 个答案:

答案 0 :(得分: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。

答案 1 :(得分:0)

问题是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