Добавление данных после отправки формы в админке Django - PullRequest
0 голосов
/ 03 ноября 2018

(Я новичок в Django и использую кодовую базу с Django 1.8)

Есть модель (Survey), которая имеет отношение многие ко многим с другой (Page) и ссылку на начальную страницу (Page). Создание этого в панели администратора было бы очень грязным для пользователя, но это может быть решено, если они примут несколько решений Да / Нет.

Итак, на странице добавления администратора я добавил несколько выбранных полей, которые не являются частью модели (более или менее «Включить раздел A?» / «Включить раздел B?» / «Включить раздел C?» ... и т.д.), и исходя из того, что пользователь выбирает здесь, я хочу добавить разные ключи к ссылкам на страницу в Survey и на стартовую страницу.

Работать с ними хорошо, но когда форма / модель сохранена, page_links и start_page отсутствуют, возможно потому, что они не являются частью формы.

Я пытался добавить их в cleaned_data (в save или clean), но это не работает.

Вот модель:

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']

По сути, я просто хочу, чтобы содержимое 'page_links' и 'start_page' в save_model находилось в экземпляре Survey в БД.

(Примечание: я знаю, что из этого следует, что я должен создать промежуточный «Раздел» и просто заставить пользователя выбирать их, но предположим, что есть причины, по которым я этого не делаю, если это возможно 8))

Любая помощь очень ценится.

Ответы [ 2 ]

0 голосов
/ 04 ноября 2018

Проблема в том, что экземпляр, который 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
0 голосов
/ 04 ноября 2018

Хорошо, оказалось, что подход, который работал, состоял в том, чтобы вернуть данные формы в обычном режиме, а затем выполнить присвоение в методе save_model admin.ModelAdmin.

Итак, форма стала:

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 в модель Admin.save_model.

...