Opened 16 years ago
Closed 14 years ago
#9150 closed (fixed)
Generic views should accept arguements for form_class
Reported by: | ErikW | Owned by: | Thomas Bechtold |
---|---|---|---|
Component: | Generic views | Version: | dev |
Severity: | Keywords: | generic views, form_class, arguments | |
Cc: | Aaron C. de Bruyn | Triage Stage: | Design decision needed |
Has patch: | yes | Needs documentation: | no |
Needs tests: | yes | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
The form_class property for generic views such a django.views.generic.create_update.* should accept either a ModelForm class or a ModelForm object. This would allow generic "wrapper views" to easily inject run-time data into the form instance.
This will be useful for sites which use contrib.auth to create "members only" area. Such sites would be able to use generic views while restricting the view's queryset to the currently logged in user. It would also make it easier for the create_object generic view to set the "user ForeignKey" to the currently logged in user behind the scenes. Right now, request.POST has to be manipulated in a wrapper view to hide this field from the end-user.
If form_class would accept ModelForm(initial={'user': request.user,}) instead of just ModelForm, this problem would be eased. Alternatively, maybe generic views could accept another property containing a dictionary of desired arguments to feed to the ModelForm object.
I think this or a similar enhancement would greatly expand the situations where generics can be used.
Attachments (2)
Change History (17)
comment:1 by , 16 years ago
milestone: | post-1.0 |
---|
comment:2 by , 16 years ago
Triage Stage: | Unreviewed → Design decision needed |
---|
comment:3 by , 15 years ago
Replying to erikcw:
The form_class property for generic views such a django.views.generic.create_update.* should accept either a ModelForm class or a ModelForm object. This would allow generic "wrapper views" to easily inject run-time data into the form instance.
This will be useful for sites which use contrib.auth to create "members only" area. Such sites would be able to use generic views while restricting the view's queryset to the currently logged in user. It would also make it easier for the create_object generic view to set the "user ForeignKey" to the currently logged in user behind the scenes. Right now, request.POST has to be manipulated in a wrapper view to hide this field from the end-user.
If form_class would accept ModelForm(initial={'user': request.user,}) instead of just ModelForm, this problem would be eased. Alternatively, maybe generic views could accept another property containing a dictionary of desired arguments to feed to the ModelForm object.
I think this or a similar enhancement would greatly expand the situations where generics can be used.
Here is an example how it should be:
models.py:
from django.db import models class Project(models.Model): name = models.CharField(max_length=200) auth_group = models.ForeignKey('auth.Group') def __unicode__(self): return self.name
forms.py:
from models import Project class ProjectForm(ModelForm): def __init__(self, request, *args, **kwargs): super(ProjectForm, self).__init__(*args, **kwargs) #set possible groups to the users groups self.fields['auth_group'].queryset = request.user.groups.all() class Meta: model = Project
views.py:
from forms import ProjectForm from django.views.generic import create_update def project_new(request): response = create_update.create_object( request, model = Project, form_class = ProjectForm(request), # actually, only this is possible: form_class = ProjectForm # but the ProjectForm requires one arguement (see forms.py) template_name = 'project_form.html', ) return response
comment:4 by , 15 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:5 by , 15 years ago
Has patch: | set |
---|---|
milestone: | → 1.1 |
Attachment attachment:create_update.py.diff is a patch for this problem.
how the patch works
The Patch adds a new parameter for the create_object() method and update_object() method. the new parameter is called form_params (type is dict). it's now possible to pass parameters to the constructor of the form.
Example
models.py
class Project(models.Model): """ A Model which describe a Project """ name = models.CharField(max_length=200) auth_group = models.ForeignKey('auth.Group') def __unicode__(self): return self.name
forms.py
class ProjectForm(ModelForm): """ Form for a project """ def __init__(self, groups, *args, **kwargs): super(ProjectForm, self).__init__(*args, **kwargs) #set possible groups to the users groups self.fields['auth_group'].queryset = groups class Meta: model = Project
ProjectForm needs as parameter the variable groups. This is normally request.user.groups.all().
views.py
def project_new(request): """ A Form to create a new Project """ response = create_update.create_object( request, form_class = ProjectForm, form_params = {'groups':request.user.groups.all(),}, template_name = 'project_form.html', ) return response def project_update(request, project_id): """ A Form to Update a Project """ response = create_update.update_object( request, form_class = ProjectForm, form_params = {'groups':request.user.groups.all(),}, template_name = 'project_form.html', object_id = int(project_id), post_save_redirect = reverse('project-list') ) #return response
in both methods, there is the var form_params passed.
project_form.html
{% if form %} <h1>{% trans "New Project" %}</h1> <form action="." method="POST"> <table> {{ form.as_table }} </table> <p><input type="submit" value="Submit"></p> </form> {% else %} {% endif %}
follow-up: 7 comment:6 by , 15 years ago
milestone: | 1.1 |
---|---|
Needs documentation: | set |
Needs tests: | set |
Patch needs improvement: | set |
Only absolutely critical bugs go into 1.1 at this point. This is a feature/new function -- and it still in design decision needed, not accepted, stage -- so 1.1 is not an appropriate milestone.
Also, the patch should be generated from the root of the source tree, and is currently lacking both documentation and tests. Please read:
http://docs.djangoproject.com/en/dev/internals/contributing/#patch-style
by , 15 years ago
Attachment: | create_update.py.diff added |
---|
Patch:add form_params={} to pass content to forms within generic views
comment:7 by , 15 years ago
Replying to kmtracey:
Only absolutely critical bugs go into 1.1 at this point. This is a feature/new function -- and it still in design decision needed, not accepted, stage -- so 1.1 is not an appropriate milestone.
ok. i understand this point. but the ticket is 10 month old. when will be a design decision?
Also, the patch should be generated from the root of the source tree, and is currently lacking both documentation and tests. Please read:
http://docs.djangoproject.com/en/dev/internals/contributing/#patch-style
the new patch fix this problem. i also added some documentation
by , 15 years ago
Attachment: | generic-views.txt.diff added |
---|
Update Documentation for generic views and add form_params as parameter
comment:8 by , 15 years ago
milestone: | → 1.2 |
---|
comment:9 by , 15 years ago
milestone: | 1.2 |
---|---|
Needs documentation: | unset |
Patch needs improvement: | unset |
comment:10 by , 15 years ago
milestone: | → 1.2 |
---|---|
Version: | 1.0 → SVN |
comment:11 by , 15 years ago
Cc: | added |
---|
comment:12 by , 15 years ago
The alternative, which requires no changes to Django, is to use a wrapper function with a dynamically created ModelForm that includes the required behaviour, as illustrated on #12392.
comment:13 by , 15 years ago
milestone: | 1.2 |
---|
1.2 is feature-frozen, moving this feature request off the milestone.
comment:14 by , 14 years ago
You can set initial data this way
from django.views.generic import create_update def custom_create(request, *args, **kwargs): ''' update __init__ of the form class to use current user as initial ''' form_class = kwargs['form_class'] class custom_class(form_class): def __init__(self, *args, **kwargs): kwargs['initial'] = {'user': request.user.id} return super(custom_class, self).__init__(*args, **kwargs) kwargs['form_class'] = custom_class return create_update.create_object(request, *args, **kwargs)
and use it the same way you use create_object generic view (except passing model to it).
(WORKSFORME)
comment:15 by , 14 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
Function-based generic views were deprecated by the introduction of class-based views in [14254]. Class-based views should solve this problem.
Milestone post-1.0 deleted