Добавление данных после отправки формы в админке Django

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

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

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

Работать с ними можно, но когда форма / модель сохраняется, 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']

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

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

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

Есть ли у ModelForm метод под названием save_model в 1.8? Разве это не просто save?

CoffeeBasedLifeform 04.11.2018 12:33

@CoffeeBasedLifeform Да, это была опечатка, только что отредактировал, спасибо.

Mark 04.11.2018 13:15
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
2
1 406
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Итак, оказалось, что подход, который сработал, заключался в том, чтобы вернуть данные формы в обычном режиме, а затем выполнить назначение в методе 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']

Это прекрасно работает (Edit), хотя мне почти наверняка следует переместить больше логики из form.save в modelAdmin.save_model.

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

Другие вопросы по теме