diff --git a/django/views/generic/detail.py b/django/views/generic/detail.py
index a2adb15..06118a9 100644
a
|
b
|
class SingleObjectMixin(ContextMixin):
|
58 | 58 | `get_object` is overridden. |
59 | 59 | """ |
60 | 60 | if self.queryset is None: |
61 | | if self.model: |
62 | | return self.model._default_manager.all() |
| 61 | model = self.get_model() |
| 62 | if model: |
| 63 | return model._default_manager.all() |
63 | 64 | else: |
64 | 65 | raise ImproperlyConfigured(u"%(cls)s is missing a queryset. Define " |
65 | 66 | u"%(cls)s.model, %(cls)s.queryset, or override " |
… |
… |
class SingleObjectMixin(ContextMixin):
|
93 | 94 | context.update(kwargs) |
94 | 95 | return super(SingleObjectMixin, self).get_context_data(**context) |
95 | 96 | |
| 97 | def get_model(self): |
| 98 | return self.model |
| 99 | |
96 | 100 | |
97 | 101 | class BaseDetailView(SingleObjectMixin, View): |
98 | 102 | def get(self, request, *args, **kwargs): |
… |
… |
class SingleObjectTemplateResponseMixin(TemplateResponseMixin):
|
133 | 137 | self.object._meta.object_name.lower(), |
134 | 138 | self.template_name_suffix |
135 | 139 | )) |
136 | | elif hasattr(self, 'model') and hasattr(self.model, '_meta'): |
137 | | names.append("%s/%s%s.html" % ( |
138 | | self.model._meta.app_label, |
139 | | self.model._meta.object_name.lower(), |
140 | | self.template_name_suffix |
141 | | )) |
| 140 | elif hasattr(self, 'get_model'): |
| 141 | model = self.get_model() |
| 142 | if hasattr(model, '_meta'): |
| 143 | names.append("%s/%s%s.html" % ( |
| 144 | model._meta.app_label, |
| 145 | model._meta.object_name.lower(), |
| 146 | self.template_name_suffix |
| 147 | )) |
142 | 148 | return names |
143 | 149 | |
144 | 150 | |
diff --git a/django/views/generic/edit.py b/django/views/generic/edit.py
index 1f488cb..33eafe7 100644
a
|
b
|
class ModelFormMixin(FormMixin, SingleObjectMixin):
|
72 | 72 | if self.form_class: |
73 | 73 | return self.form_class |
74 | 74 | else: |
75 | | if self.model is not None: |
76 | | # If a model has been explicitly provided, use it |
77 | | model = self.model |
78 | | elif hasattr(self, 'object') and self.object is not None: |
79 | | # If this view is operating on a single object, use |
80 | | # the class of that object |
81 | | model = self.object.__class__ |
82 | | else: |
83 | | # Try to get a queryset and extract the model class |
84 | | # from that |
85 | | model = self.get_queryset().model |
| 75 | model = self.get_model() |
86 | 76 | return model_forms.modelform_factory(model) |
87 | 77 | |
88 | 78 | def get_form_kwargs(self): |
… |
… |
class ModelFormMixin(FormMixin, SingleObjectMixin):
|
118 | 108 | context[context_object_name] = self.object |
119 | 109 | context.update(kwargs) |
120 | 110 | return super(ModelFormMixin, self).get_context_data(**context) |
| 111 | |
| 112 | def get_model(self): |
| 113 | if self.model: |
| 114 | return self.model |
| 115 | elif self.form_class and issubclass(self.form_class, model_forms.ModelForm): |
| 116 | return self.form_class._meta.model |
| 117 | elif hasattr(self, 'object') and self.object is not None: |
| 118 | return self.object.__class__ |
| 119 | else: |
| 120 | return self.get_queryset().model |
121 | 121 | |
122 | 122 | |
123 | 123 | class ProcessFormView(View): |
diff --git a/tests/regressiontests/generic_views/edit.py b/tests/regressiontests/generic_views/edit.py
index 651e14f..90cbe04 100644
a
|
b
|
class CreateViewTests(TestCase):
|
87 | 87 | self.assertRedirects(res, reverse('author_detail', kwargs={'pk': obj.pk})) |
88 | 88 | self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe>']) |
89 | 89 | |
| 90 | def test_create_with_only_form_class(self): |
| 91 | res = self.client.get('/edit/artists/create/formclass/') |
| 92 | self.assertEqual(res.status_code, 200) |
| 93 | self.assertTrue(isinstance(res.context['form'], views.ArtistForm)) |
| 94 | self.assertFalse('object' in res.context) |
| 95 | self.assertFalse('artist' in res.context) |
| 96 | self.assertTemplateUsed(res, 'generic_views/artist_form.html') |
| 97 | |
| 98 | res = self.client.post('/edit/artists/create/formclass/', |
| 99 | {'name': 'Rene Magritte'}) |
| 100 | self.assertEqual(res.status_code, 302) |
| 101 | artist = Artist.objects.get(name='Rene Magritte') |
| 102 | self.assertRedirects(res, 'http://testserver/detail/artist/%d/' % artist.pk) |
| 103 | self.assertQuerysetEqual(Artist.objects.all(), ['<Artist: Rene Magritte>']) |
| 104 | |
90 | 105 | def test_create_without_redirect(self): |
91 | 106 | try: |
92 | 107 | res = self.client.post('/edit/authors/create/naive/', |
diff --git a/tests/regressiontests/generic_views/forms.py b/tests/regressiontests/generic_views/forms.py
index a78242f..3d7079e 100644
a
|
b
|
from __future__ import absolute_import
|
2 | 2 | |
3 | 3 | from django import forms |
4 | 4 | |
5 | | from .models import Author |
| 5 | from .models import Author, Artist |
6 | 6 | |
7 | 7 | |
8 | 8 | class AuthorForm(forms.ModelForm): |
… |
… |
class AuthorForm(forms.ModelForm):
|
11 | 11 | |
12 | 12 | class Meta: |
13 | 13 | model = Author |
| 14 | |
| 15 | |
| 16 | class ArtistForm(forms.ModelForm): |
| 17 | name = forms.CharField() |
| 18 | |
| 19 | class Meta: |
| 20 | model = Artist |
diff --git a/tests/regressiontests/generic_views/urls.py b/tests/regressiontests/generic_views/urls.py
index 090ec73..93c49d4 100644
a
|
b
|
urlpatterns = patterns('',
|
56 | 56 | # Create/UpdateView |
57 | 57 | (r'^edit/artists/create/$', |
58 | 58 | views.ArtistCreate.as_view()), |
| 59 | (r'^edit/artists/create/formclass/$', |
| 60 | views.ArtistOnlyFormClassCreate.as_view()), |
59 | 61 | (r'^edit/artists/(?P<pk>\d+)/update/$', |
60 | 62 | views.ArtistUpdate.as_view()), |
61 | 63 | |
diff --git a/tests/regressiontests/generic_views/views.py b/tests/regressiontests/generic_views/views.py
index 1e70ba4..25c80b3 100644
a
|
b
|
from django.core.urlresolvers import reverse
|
6 | 6 | from django.utils.decorators import method_decorator |
7 | 7 | from django.views import generic |
8 | 8 | |
9 | | from .forms import AuthorForm |
| 9 | from .forms import AuthorForm, ArtistForm |
10 | 10 | from .models import Artist, Author, Book, Page |
11 | 11 | |
12 | 12 | |
… |
… |
class AuthorListCustomPaginator(AuthorList):
|
78 | 78 | class ArtistCreate(generic.CreateView): |
79 | 79 | model = Artist |
80 | 80 | |
| 81 | class ArtistOnlyFormClassCreate(generic.CreateView): |
| 82 | form_class = ArtistForm |
| 83 | |
81 | 84 | |
82 | 85 | class NaiveAuthorCreate(generic.CreateView): |
83 | 86 | queryset = Author.objects.all() |