使用基于类的通用视图DetailView与ModelForm显示一个错误 - 如何继续?

时间:2011-07-03 16:27:35

标签: django django-generic-views modelform bug-reporting

令我印象深刻的是,功能性网站可以在教程中与通用视图一起快速完成。此外,表单处理的工作流程很好。我使用ModelForm帮助器类从我制作的模型创建一个表单,并很高兴看到这么多的功能汇集在一起​​。当我使用通用list_detail.object_detail时,我很失望,我可以显示的所有内容都是单独的字段。我知道ModelForm类包含渲染信息,所以我想将ModelForm与通用视图一起使用。

我正在询问stackoverflow以获得一些方向,并欣赏几张海报的答案和评论。我已经想出如何让它工作,但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行上的代码被执行并且出现错误消息&lt;'ModelFormOptions'对象没有属性'app_label'&gt;。我得出结论,定义了_meta对象。我想问题是在ModelForm中定义了Meta类。该Meta可能没有设置预期的字段。解决方法是重写get_template_names并返回正确的模板。

我是Django和Python的新手。我很感谢贡献者在我之前提出的以下问题中给出的答案和评论。 ( Putting links in list_detail.object_list to list_detail.object_detailUsing form in object_detailRolling your own generic views in Django

我该怎么做才能报告错误?

1 个答案:

答案 0 :(得分:2)

我相信你是对的。这是一个错误,它源于ModelForm和Models都具有_meta属性的事实。每当从get_object()返回包含_meta属性的对象时,同样的错误就会出现。

get_object不必返回Model实例。您可以通过查看DetailView的来源并阅读它的docstring:

来确认这一点
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()`.
    """

请注意,doc字符串明确表示重写self.get_object()支持任何对象。

另一条确凿的证据来自这个错误本身发生的位置,即get_template_names method的{​​{1}}。

SingleObjectTemplateResponseMixin

再次查看此代码,评论本身会说“如果有问题的对象是模型”。从这个评论我们可以推断出对象并不总是必须是模型。

但是,如果您尝试创建允许某人编辑/创建/删除模型的视图,您应该查看包含FormView,CreateView,EditView和DeleteView的编辑视图。您可以在https://docs.djangoproject.com/en/1.3/ref/class-based-views/#editing-views找到更多相关信息。

要回答有关如何举报错误的问题,请遵循https://docs.djangoproject.com/en/1.3/internals/contributing/#reporting-bugs详细说明的指南。