(Я новичок в 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))
Любая помощь очень ценится.
@CoffeeBasedLifeform Да, это была опечатка, только что отредактировал, спасибо.






Итак, оказалось, что подход, который сработал, заключался в том, чтобы вернуть данные формы в обычном режиме, а затем выполнить назначение в методе 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
Есть ли у
ModelFormметод под названиемsave_modelв 1.8? Разве это не простоsave?