Using a generic view of the DetailView class with ModelForm shows an error - how to proceed?

I was impressed with how quickly a functional website can fit in with the general views in textbooks. In addition, the workflow for processing forms is good. I used the ModelForm helper class to create a form from a model I made, and was glad to see so many functions coming together. When I used the general list_detail.object_detail list, I was disappointed that all I could display were fields individually. I knew that the ModelForm class contains information for rendering, so I wanted to use ModelForm with a general view.

I asked about stackoverflow to get some direction, and appreciate the answers and comments of several posters. I figured out how to make this work, but there is an error in DetailView. The solution includes a workaround.

To use the ModelView with a general view and automatically display all fields, follow these steps:

Create a project and create a stationary application in it.

If you

# 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 

and

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

and

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

it works. Instructions for using general class representations are based on https://docs.djangoproject.com/en/1.3/topics/class-based-views/ The instructions there are pretty clear. The key to working on this is to override get_object. In the documentation in the β€œDoing More Work” section, she describes how to do this, the steps involved in invoking the original version of get_object, and then the additional work. I realized that the returned object could be a ModelForm object. The object that get_object returns gets directly into the template in rendering. By accepting the received stationary object and launching it through InpatientForm, it can be passed to the view as a form, which then displays itself.

As for the error: the error in DetailView is that the get_template_names function is trying to create a template name from a structure that does not exist. At https://code.djangoproject.com/browser/django/trunk/django/views/generic/detail.py in lines 127 to 140 we have inside 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 )) 

The error is that the code on line 131 executes and dies with an error message. The 'ModelFormOptions' object does not have the 'app_label'> attribute. I conclude that the _meta object is defined. I believe the problem is that the Meta class is defined in ModelForm. Meta probably has no fields defined. The workaround is to simply rewrite get_template_names and return the correct template.

I am new to Django and Python. I appreciate the answers and comments of the participants in the following previous questions that I asked. ( Entering links in list_detail.object_list in list_detail.object_detail , Using the form in object_detail , Moving your own shared views in Django )

What should I do to report an error?

+6
source share
1 answer

You are right, I believe. This is a bug due to the fact that both ModelForm and Models have the _meta attribute. The same error will be displayed at any time when the object returns from get_object() , which contains the _meta attribute.

get_object does not need to return an instance of the model. You can confirm this by looking at the DetailView source and reading its 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()`. """ 

Note that the doc line explicitly indicates that any object is supported by the self.get_object () override.

Another piece of supporting evidence is where this error occurs, which is the get_template_names method of 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 )) 

Looking at this code again, the comment itself says: "If the object in question is a model." From this comment, we can conclude that an object does not always have to be a model.

However, if you are trying to create a view that allows someone to edit / create / delete a model, you really need to take a look at Editing Views, which include FormView, CreateView, EditView and DeleteView. You can see additional information for them https://docs.djangoproject.com/en/1.3/ref/class-based-views/#editing-views .

To answer the question about how to report an error, you must follow the instructions given at https://docs.djangoproject.com/en/1.3/internals/contributing/#reporting-bugs .

+2
source

Source: https://habr.com/ru/post/891910/


All Articles