Использование базового представления на основе классов DetailView с ModelForm выявляет ошибку - как поступить?
Я был впечатлен тем, как быстро функционирующий веб-сайт может сочетаться с общими представлениями в учебных пособиях. Кроме того, рабочий процесс для обработки форм это хорошо. Я использовал вспомогательный класс ModelForm для создания формы из модели, которую я сделал, и был рад видеть, что так много функциональности собралось вместе. Когда я использовал общий list_detail.object_detail, я был разочарован тем, что все, что я мог отобразить, было полями по отдельности. Я знал, что класс ModelForm содержит информацию для рендеринга, поэтому я хотел использовать ModelForm с общим представлением.
Я расспрашивал о stackru, чтобы получить какое-то направление, и ценю ответы и комментарии от нескольких авторов. Я понял, как заставить это работать, но в DetailView есть ошибка. Решение включает обходной путь.
Чтобы использовать ModelView с общим представлением и получить все поля для автоматической визуализации, выполните следующие работы:
Создайте проект, и в нем создайте приложение для стационарных пациентов.
Если у вас есть
# inpatients/models.py
class Inpatient(models.Model):
last_name = models.CharField(max_length=30)
first_name = models.CharField(max_length=30,blank=True)
address = models.CharField(max_length=50,blank=True)
city = models.CharField(max_length=60,blank=True)
state = models.CharField(max_length=30,blank=True)
DOB = models.DateField(blank=True,null=True)
notes = models.TextField(blank=True)
def __unicode__(self):
return u'%s, %s %s' % (self.last_name, self.first_name, self.DOB)
class InpatientForm(ModelForm):
class Meta:
model = Inpatient
а также
# inpatients/views.py
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render_to_response
from django.views.generic import DetailView
from portal.inpatients.models import *
def formtest(request):
if request.method == 'POST':
form = InpatientForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/inpatients')
else:
form = InpatientForm()
return render_to_response("formtest.html", {'form': form})
class FormDetailView(DetailView):
model=Inpatient
context_object_name='inpatient' # defines the name in the template
template_name_field='inpatient_list_page.html'
def get_object(self):
inpatient=super(FormDetailView,self).get_object()
form=InpatientForm(instance=inpatient)
return form
def get_template_names(self):
return ['inpatient_list_page.html',]
а также
#urls.py
from django.conf.urls.defaults import patterns, include, url
from django.views.generic import ListView
from portal.inpatients.models import Inpatient, InpatientForm
from portal.inpatients.views import FormDetailView
urlpatterns = patterns('',
(r'^formtest/$','portal.inpatients.views.formtest'),
(r'^inpatients/$', ListView.as_view(
model=Inpatient, template_name='inpatient_list_page.html')),
(r'^inpatient-detail/(?P<pk>\d+)/$', FormDetailView.as_view()),
)
# with a template containing
{% block content %}
<h2>Inpatients</h2>
<ul>
{% for aninpatient in object_list %}
<li><a href='/inpatient-detail/{{ aninpatient.id }}/'>
{{ aninpatient }}, id={{ aninpatient.id }}</a></li>
{% endfor %}
</ul>
{{ inpatient.as_p }}
{% endblock %}
# Yeah, kind of hokey. The template is for both the list view and detail view.
# Note how the form is rendered with one line - {{ inpatient.as_p }}
оно работает. Инструкции по использованию общих представлений на основе классов можно найти по адресу https://docs.djangoproject.com/en/1.3/topics/class-based-views/ Инструкции там довольно ясны. Ключом к тому, чтобы заставить вещи работать, является переопределение get_object. В документации, приведенной в разделе "Выполнение дополнительной работы", в ней подробно описывается, как это сделать, и при этом необходимо вызвать исходную версию get_object, а затем выполнить дополнительную работу. Я понял, что возвращаемый объект может быть объектом ModelForm. Объект, который возвращает get_object, попадает прямо в шаблон рендера. Взяв извлеченный стационарный объект и запустив его через InpatientForm, он может быть передан в представление как форма, которая затем визуализирует себя.
Что касается ошибки: Ошибка в DetailView заключается в том, что функция get_template_names пытается создать имя шаблона из несуществующей структуры. В https://code.djangoproject.com/browser/django/trunk/django/views/generic/detail.py по строкам 127–140 мы имеем в SingleObjectTemplateResponseMixin.get_template_names:
127 # The least-specific option is the default <app>/<model>_detail.html;
128 # only use this if the object in question is a model.
129 if hasattr(self.object, '_meta'):
130 names.append("%s/%s%s.html" % (
131 self.object._meta.app_label,
132 self.object._meta.object_name.lower(),
133 self.template_name_suffix
134 ))
135 elif hasattr(self, 'model') and hasattr(self.model, '_meta'):
136 names.append("%s/%s%s.html" % (
137 self.model._meta.app_label,
138 self.model._meta.object_name.lower(),
139 self.template_name_suffix
140 ))
Ошибка состоит в том, что код в строке 131 выполняется и умирает с сообщением об ошибке <объект 'ModelFormOptions' не имеет атрибута 'app_label'>. Я делаю вывод, что объект _meta определен. Я предполагаю, что проблема в том, что в ModelForm класс Meta определен. У этой меты, вероятно, нет тех полей, которые ожидаются. Обходной путь - просто переписать get_template_names и вернуть правильный шаблон.
Я новичок в Django и Python. Я ценю ответы и комментарии участников на следующих предыдущих вопросах, которые я задавал. ( Помещение ссылок в list_detail.object_list в list_detail.object_detail, использование формы в object_detail, переход на собственные общие представления в Django)
Что я должен сделать, чтобы сообщить об ошибке?
1 ответ
Вы правы, я верю. Эта ошибка связана с тем, что и ModelForm, и Models имеют атрибут _meta. Эта же ошибка будет проявляться каждый раз, когда объект возвращается из get_object()
который содержит атрибут _meta.
get_object
не должен возвращать экземпляр модели. Вы можете подтвердить это, посмотрев на источник DetailView
и читая эту строку документации:
class DetailView(SingleObjectTemplateResponseMixin, BaseDetailView):
"""
Render a "detail" view of an object.
By default this is a model instance looked up from `self.queryset`, but the
view will support display of *any* object by overriding `self.get_object()`.
"""
Обратите внимание, что в строке документа явно указано, что любой объект поддерживается путем переопределения self.get_object().
Еще одним подтверждением являются данные о месте, где произошла ошибка get_template_names method
из SingleObjectTemplateResponseMixin
,
# The least-specific option is the default <app>/<model>_detail.html;
# only use this if the object in question is a model.
if hasattr(self.object, '_meta'):
names.append("%s/%s%s.html" % (
self.object._meta.app_label,
self.object._meta.object_name.lower(),
self.template_name_suffix
))
elif hasattr(self, 'model') and hasattr(self.model, '_meta'):
names.append("%s/%s%s.html" % (
self.model._meta.app_label,
self.model._meta.object_name.lower(),
self.template_name_suffix
))
Снова глядя на этот код, сам комментарий говорит: "Если рассматриваемый объект является моделью". Из этого комментария мы можем сделать вывод, что объект не всегда должен быть моделью.
Однако, если вы пытаетесь создать вид, который позволяет кому-либо редактировать / создавать / удалять модель, вам действительно следует взглянуть на редактирующие виды, которые включают FormView, CreateView, EditView и DeleteView. Вы можете увидеть больше информации об этом на https://docs.djangoproject.com/en/1.3/ref/class-based-views/.
Чтобы ответить на вопрос о том, как сообщить об ошибке, следуйте инструкциям, приведенным на https://docs.djangoproject.com/en/1.3/internals/contributing/.