2011-07-03 18 views
6

me han impresionado la rapidez con que un sitio web funcional puede ir de la mano con las vistas genéricas en los tutoriales. Además, el flujo de trabajo para el procesamiento de formularios es agradable. Usé la clase de ayuda ModelForm para crear un formulario a partir de un modelo que hice y me complació ver que se unía tanta funcionalidad. Cuando utilicé el genérico list_detail.object_detail, me decepcionó que todo lo que podía mostrar eran campos individualmente. Sabía que la clase ModelForm contenía información para el procesamiento, por lo que quería usar ModelForm con una vista genérica.clase basada vista genérica DetailView con un ModelForm revela un error - la forma de proceder?

que estaba pidiendo alrededor en stackoverflow para conseguir una cierta dirección, y apreciar las respuestas y comentarios de varios carteles. He descubierto cómo hacer que esto funcione, pero hay un error en DetailView. La solución incluye una solución alternativa.

Para utilizar un modelview con la vista genérica y obtener todos los campos para hacer automáticamente las siguientes obras:

crear un proyecto, y en ella los pacientes hospitalizados crear aplicaciones.

Si tiene

# 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 

y

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

y

#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 }} 

funciona. Las instrucciones para usar vistas genéricas basadas en clases viven en https://docs.djangoproject.com/en/1.3/topics/class-based-views/. Las instrucciones son bastante claras. La clave para hacer que las cosas funcionen es redefinir get_object. En la documentación en la sección "Realización de un trabajo extra" que describe muy bien cómo hacer esto, los pasos son para llamar a la versión original de get_object, y luego al trabajo extra. El bit que me di cuenta es que el objeto de retorno puede ser un objeto ModelForm. El objeto que devuelve get_object va directamente a la plantilla en un render. Al tomar el objeto hospitalario recuperado y ejecutarlo a través de InpatientForm, se puede pasar a una vista como un formulario que luego se renderiza.

En cuanto al error: El error en DetailView es que la función get_template_names intenta hacer un nombre de plantilla de una estructura que no existe. En https://code.djangoproject.com/browser/django/trunk/django/views/generic/detail.py en las líneas 127-140 tenemos dentro 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   )) 

El error es que el código en la línea 131 se ejecuta y muere con el mensaje de error objeto < '' ModelFormOptions no tiene atributo 'app_label' >. Concluyo que el objeto _meta está definido. Supongo que el problema es que en un ModelForm se define la clase Meta. Ese Meta probablemente no tiene los campos establecidos que se esperan. La solución consiste simplemente en volver a escribir get_template_names y devolver la plantilla correcta.

Soy nuevo en Django y Python. Agradezco las respuestas y los comentarios de los colaboradores en las siguientes preguntas previas que hice. ( Putting links in list_detail.object_list to list_detail.object_detail, Using form in object_detail, Rolling your own generic views in Django)

¿Qué debo hacer para informar del fallo?

+0

No creo que sea un error, y creo que 'get_object' siempre debe devolver la instancia del modelo no la instancia' ModelForm'. Intente usar [editar CBV] (https://docs.djangoproject.com/en/dev/ref/class-based-views/#editing-views). –

+0

Creo que es un error por varias razones. La documentación no dice que no es válida. La prueba de datos válidos antes de la asignación prueba la existencia de _meta en lugar de los campos reales. La rutina que está buscando la plantilla no encontró la plantilla. Además, en el principal de Do not Repeat Yourself, el ModelForm debe poder entregarse en una plantilla para su representación. – kd4ttc

Respuesta

2

tiene usted razón que creo. Este es un error que se deriva del hecho de que tanto ModelForm como Models tienen un atributo _meta.Este mismo error se mostraría cada vez que se devuelva un objeto desde get_object() que contiene un atributo _meta.

get_object no tiene que devolver una instancia de modelo. Puede confirmar esto mirando la fuente para DetailView y la lectura se 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()`. 
    """ 

en cuenta que la cadena de documentación dice explícitamente que cualquier objeto es apoyado por anulando self.get_object().

Otra prueba que lo corrobora es la ubicación donde se produce este error, que es el get_template_names method de 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 
     )) 

Mirando nuevamente este código, el comentario en sí mismo dice "Si el objeto en cuestión es un modelo". De este comentario podemos inferir que el objeto no siempre tiene que ser un modelo.

Sin embargo, si intenta crear una vista que permita a alguien editar/crear/borrar un modelo, debería echar un vistazo a las Vistas de edición que incluyen FormView, CreateView, EditView y DeleteView. Puede ver más información al respecto en el https://docs.djangoproject.com/en/1.3/ref/class-based-views/#editing-views.

Para responder a la pregunta sobre cómo informar el error, debe seguir las instrucciones detalladas en https://docs.djangoproject.com/en/1.3/internals/contributing/#reporting-bugs.

+0

Acepto el problema de edición/eliminación. Visualizo una pantalla automática ya que he seguido los controles en esa página para mostrar las funciones de edición/eliminación. Por supuesto, utilizaré las vistas genéricas tanto como sea posible. Aprecio que corroboren mi idea de lo que debería permitirse. Si Django sigue el camino de muchos proyectos, agregará complejidad adicional y luego, cuando se llegue a un punto crítico, surgirá una reescritura que es más simple y permite una mayor funcionalidad. Ya hemos pasado de función basada en clase. Estoy seguro de que muchos de Django ya han pasado por eso. Gracias, Steve – kd4ttc

+0

Todavía no estoy convencido de que esto sea un error. No debe usar 'DetailView' donde' FormView' es más apropiado para una cosa. En cuanto a 'get_template_names' podrían haber probado con' isinstance', en cuyo caso debe devolver la instancia de Model para descubrir el nombre de la plantilla. De esta forma puedes devolver cualquier cosa con '_meta' y ser capaz de obtener el nombre de la plantilla. Excepto por la forma. Deberías usar vistas de formulario con él. –

+0

Este es un error porque ocurrirá con CUALQUIER clase que tenga una instancia _meta que no sea un modelo. No tiene que ser una clase de formulario (que es más apropiado para usar un FormView para. –

Cuestiones relacionadas