Ticket #16256: ticket16256.patch
File ticket16256.patch, 24.8 KB (added by , 13 years ago) |
---|
-
AUTHORS
diff --git a/AUTHORS b/AUTHORS index 8cb71c1..fae6f81 100644
a b answer newbie questions, and generally made Django that much better: 412 412 Luciano Ramalho 413 413 Amit Ramon <amit.ramon@gmail.com> 414 414 Philippe Raoult <philippe.raoult@n2nsoft.com> 415 Iván Raskovsky <raskovsky@gmail.com> 415 416 Massimiliano Ravelli <massimiliano.ravelli@gmail.com> 416 417 Brian Ray <http://brianray.chipy.org/> 417 418 Łukasz Rekucki <lrekucki@gmail.com> -
django/views/generic/__init__.py
diff --git a/django/views/generic/__init__.py b/django/views/generic/__init__.py index 1a98067..5ef8d97 100644
a b from django.views.generic.detail import DetailView 6 6 from django.views.generic.edit import FormView, CreateView, UpdateView, DeleteView 7 7 from django.views.generic.list import ListView 8 8 9 from django.views.generic.formsets import (FormSetsView, ModelFormSetsView, 10 InlineFormSetsView, EnhancedFormSet, 11 EnhancedModelFormSet, EnhancedInlineFormSet, ) 12 9 13 10 14 class GenericViewError(Exception): 11 15 """A problem in a generic view.""" -
new file django/views/generic/formsets.py
diff --git a/django/views/generic/formsets.py b/django/views/generic/formsets.py new file mode 100644 index 0000000..da9c2ad
- + 1 from django.http import HttpResponseRedirect 2 from django.core.exceptions import ImproperlyConfigured 3 from django.forms.formsets import formset_factory, BaseFormSet, all_valid 4 from django.forms.models import (modelformset_factory, inlineformset_factory, 5 BaseModelFormSet, BaseInlineFormSet, ModelForm) 6 7 from django.views.generic.base import View, TemplateResponseMixin 8 from django.views.generic.detail import SingleObjectTemplateResponseMixin 9 from django.views.generic.edit import ModelFormMixin 10 11 12 class EnhancedFormSet(object): 13 """ 14 A base class for generic formsets 15 """ 16 17 form_class = None 18 formset_class = BaseFormSet 19 20 # formset_factory kwargs 21 extra = 3 22 can_order = False 23 can_delete = False 24 max_num = None 25 26 def get_base_formset(self, **kwargs): 27 """ 28 Returns the base formset 29 """ 30 new_kwargs = self.get_kwargs() 31 new_kwargs.update(**kwargs) 32 return self.get_factory()(**new_kwargs) 33 34 def get_factory(self): 35 """ 36 Returns the factory used to construct the formsets 37 """ 38 return formset_factory 39 40 def get_form_class(self): 41 return self.form_class 42 43 def get_formset_class(self): 44 return self.formset_class 45 46 def get_kwargs(self): 47 return {'form': self.get_form_class(), 48 'formset': self.get_formset_class(), 49 'extra': self.extra, 50 'can_order': self.can_order, 51 'can_delete': self.can_delete, 52 'max_num': self.max_num, } 53 54 55 class EnhancedModelFormSet(EnhancedFormSet): 56 """ 57 A base class for generic model formsets 58 """ 59 # TODO: provide a hook for formfield_callback 60 61 form_class = ModelForm 62 formset_class = BaseModelFormSet 63 model = None 64 queryset = None 65 fields = None 66 exclude = None 67 68 def get_factory(self): 69 return modelformset_factory 70 71 def get_model(self): 72 if self.model: 73 return self.model 74 else: 75 try: 76 return self.get_form_class().Meta.model 77 except AttributeError: 78 raise ImproperlyConfigured( 79 "No model to create the modelformset. Provide one.") 80 81 def get_queryset(self): 82 return self.queryset 83 84 def get_fields(self): 85 return self.fields 86 87 def get_exclude(self): 88 return self.exclude 89 90 def get_kwargs(self): 91 kwargs = super(EnhancedModelFormSet, self).get_kwargs() 92 kwargs.update({ 93 'model': self.get_model(), 94 'fields': self.get_fields(), 95 'exclude': self.get_exclude(), 96 }) 97 return kwargs 98 99 100 class EnhancedInlineFormSet(EnhancedModelFormSet): 101 """ 102 A base class for generic inline formsets 103 """ 104 105 fk_name = None 106 formset_class = BaseInlineFormSet 107 108 def get_factory(self): 109 return inlineformset_factory 110 111 def get_fk_name(self): 112 return self.fk_name 113 114 def get_kwargs(self): 115 kwargs = super(EnhancedInlineFormSet, self).get_kwargs() 116 kwargs.update({ 117 'fk_name': self.get_fk_name(), 118 }) 119 return kwargs 120 121 122 class FormSetsMixin(object): 123 """ 124 A mixin that provides a way to show and handle formsets 125 """ 126 127 formsets = [] # must be a list of BaseGenericFormSet 128 success_url = None 129 130 def __init__(self, *args, **kwargs): 131 self.instantiate_enhanced_formsets() 132 133 def instantiate_enhanced_formsets(self): 134 """ 135 Instantiates the enhanced formsets 136 """ 137 self.enhanced_formsets_instances = [] 138 for formset in self.formsets: 139 enhanced_formset_instance = formset() 140 self.enhanced_formsets_instances.append(enhanced_formset_instance) 141 142 def construct_formsets(self): 143 """ 144 Constructs the formsets 145 """ 146 self.formsets_instances = [] 147 148 prefixes = {} 149 for enhanced_formset in self.enhanced_formsets_instances: 150 base_formset = enhanced_formset.get_base_formset( 151 **self.get_factory_kwargs()) 152 153 # calculate prefix 154 prefix = base_formset.get_default_prefix() 155 prefixes[prefix] = prefixes.get(prefix, 0) + 1 156 if prefixes[prefix] != 1: 157 prefix = "%s-%s" % (prefix, prefixes[prefix]) 158 159 self.formsets_instances.append( 160 base_formset(prefix=prefix, **self.get_formsets_kwargs( 161 enhanced_formset)) 162 ) 163 164 def get_factory_kwargs(self): 165 """ 166 Returns the keyword arguments for the formsets factory 167 """ 168 return {} 169 170 def get_formsets_kwargs(self, enhanced_formset): 171 """" 172 Returns the keyword arguments for instanciating the formsets 173 """ 174 175 # default kwargs 176 kwargs = {} 177 178 if self.request.method in ('POST', 'PUT'): 179 kwargs.update({ 180 'data': self.request.POST, 181 'files': self.request.FILES, 182 }) 183 return kwargs 184 185 def get_context_data(self, **kwargs): 186 context_data = { 187 'formsets': [formset for formset in self.formsets_instances], 188 } 189 190 context_data.update(kwargs) 191 return context_data 192 193 def get_success_url(self): 194 if self.success_url: 195 url = self.success_url 196 else: 197 raise ImproperlyConfigured( 198 "No URL to redirect to. Provide a success_url") 199 return url 200 201 def formsets_valid(self): 202 return HttpResponseRedirect(self.get_success_url()) 203 204 def formsets_invalid(self): 205 return self.render_to_response(self.get_context_data()) 206 207 208 class ModelFormSetsMixin(FormSetsMixin): 209 """ 210 A mixin that provides a way to show and handle model formsets 211 """ 212 213 def get_formsets_kwargs(self, enhanced_formset): 214 """" 215 Returns the keyword arguments for instanciating the model formsets 216 """ 217 kwargs = super(ModelFormSetsMixin, self).get_formsets_kwargs( 218 enhanced_formset) 219 kwargs.update({ 220 'queryset': enhanced_formset.get_queryset() 221 }) 222 return kwargs 223 224 def formsets_valid(self): 225 # FIXME: beware of m2m 226 for formset in self.formsets_instances: 227 formset.save() 228 return super(ModelFormSetsMixin, self).formsets_valid() 229 230 231 class InlineFormSetsMixin(ModelFormSetsMixin, ModelFormMixin): 232 """ 233 A mixin that provides a way to show and handle a model with it's inline 234 formsets 235 """ 236 def get_formsets_kwargs(self, enhanced_formset): 237 """" 238 Returns the keyword arguments for instanciating the inline formsets 239 """ 240 kwargs = super(InlineFormSetsMixin, self).get_formsets_kwargs( 241 enhanced_formset) 242 kwargs.update({ 243 'instance': self.object 244 }) 245 return kwargs 246 247 def get_context_data(self, **kwargs): 248 """ 249 Adds the context data from both parents 250 """ 251 context_data = ModelFormSetsMixin.get_context_data(self) 252 context_data.update(ModelFormMixin.get_context_data(self, **kwargs)) 253 return context_data 254 255 def get_factory_kwargs(self): 256 """ 257 Returns the keyword arguments for the formsets factory 258 """ 259 return { 260 'parent_model': self.object.__class__, 261 } 262 263 def form_valid(self, form): 264 self.object.save() 265 form.save_m2m() 266 for formset in self.formsets_instances: 267 formset.save() 268 269 return HttpResponseRedirect(self.get_success_url()) 270 271 def form_invalid(self, form): 272 return self.render_to_response(self.get_context_data(form=form)) 273 274 275 class ProcessFormSetsView(View): 276 """ 277 A mixin that processes formsets on POST 278 """ 279 def get(self, request, *args, **kwargs): 280 self.construct_formsets() 281 return self.render_to_response(self.get_context_data()) 282 283 def post(self, request, *args, **kwargs): 284 self.construct_formsets() 285 if all_valid(self.formsets_instances): 286 return self.formsets_valid() 287 else: 288 return self.formsets_invalid() 289 290 def put(self, request, *args, **kwargs): 291 return self.post(*args, **kwargs) 292 293 294 class ProcessInlineFormSetsView(View): 295 """ 296 A mixin that processes a model instance and it's inline formsets on POST 297 """ 298 299 def get(self, request, *args, **kwargs): 300 # Create or Update 301 try: 302 self.object = self.get_object() 303 except AttributeError: 304 self.object = self.model() 305 306 # ProcessFormView 307 form_class = self.get_form_class() 308 form = self.get_form(form_class) 309 310 # ProcessFormSetsView 311 self.construct_formsets() 312 313 return self.render_to_response(self.get_context_data(form=form)) 314 315 def post(self, request, *args, **kwargs): 316 # Create or Update 317 try: 318 self.object = self.get_object() 319 except AttributeError: 320 self.object = self.model() 321 322 # ProcessFormView 323 form_class = self.get_form_class() 324 form = self.get_form(form_class) 325 326 if form.is_valid(): 327 self.object = form.save(commit=False) 328 329 # ProcessFormSetsViewV 330 self.construct_formsets() 331 332 if all_valid(self.formsets_instances): 333 return self.form_valid(form) 334 else: 335 # ProcessFormSetsViewV 336 self.construct_formsets() 337 return self.form_invalid(form) 338 339 340 def put(self, request, *args, **kwargs): 341 return self.post(*args, **kwargs) 342 343 344 class BaseFormSetsView(FormSetsMixin, ProcessFormSetsView): 345 """ 346 A base view for displaying formsets 347 """ 348 349 350 class BaseModelFormSetsView(ModelFormSetsMixin, ProcessFormSetsView): 351 """ 352 A base view for displaying model formsets 353 """ 354 355 356 class BaseInlineFormSetsView(InlineFormSetsMixin, ProcessInlineFormSetsView): 357 """ 358 A base view for displaying a model instance with it's inline formsets 359 """ 360 361 362 class FormSetsView(TemplateResponseMixin, BaseFormSetsView): 363 """ 364 A view for displaying formsets, and rendering a template response 365 """ 366 367 368 class ModelFormSetsView(TemplateResponseMixin, BaseModelFormSetsView): 369 """ 370 A view for displaying model formsets, and rendering a template response 371 """ 372 373 374 class InlineFormSetsView(SingleObjectTemplateResponseMixin, 375 BaseInlineFormSetsView): 376 """ 377 A view for displaying a model instance with it's inline formsets, and 378 rendering a template response 379 """ 380 template_name_suffix = '_form' 381 -
tests/regressiontests/generic_views/forms.py
diff --git a/tests/regressiontests/generic_views/forms.py b/tests/regressiontests/generic_views/forms.py index 7200947..b6ee715 100644
a b 1 1 from django import forms 2 from django.forms.formsets import formset_factory 2 3 3 from regressiontests.generic_views.models import Author 4 from django.views.generic import (EnhancedFormSet, EnhancedModelFormSet, 5 EnhancedInlineFormSet, ) 6 from regressiontests.generic_views.models import Author, Article 4 7 5 8 6 9 class AuthorForm(forms.ModelForm): … … class AuthorForm(forms.ModelForm): 9 12 10 13 class Meta: 11 14 model = Author 15 16 17 class ArticleForm(forms.ModelForm): 18 class Meta: 19 model = Article 20 exclude = ('author', ) 21 22 23 class ArticleEnhancedFormSet(EnhancedFormSet): 24 form_class = ArticleForm 25 26 27 class AuthorEnhancedFormSet(EnhancedFormSet): 28 form_class = AuthorForm 29 30 31 class ArticleEnhancedModelFormSet(EnhancedModelFormSet): 32 model = Article 33 34 35 class AuthorEnhancedModelFormSet(EnhancedModelFormSet): 36 model = Author 37 38 39 class ArticleEnhancedInlineFormSet(EnhancedInlineFormSet): 40 model = Article -
new file tests/regressiontests/generic_views/formsets.py
diff --git a/tests/regressiontests/generic_views/formsets.py b/tests/regressiontests/generic_views/formsets.py new file mode 100644 index 0000000..c9ae954
- + 1 from django.test import TestCase 2 from django.core.exceptions import ImproperlyConfigured 3 from django.views.generic.formsets import (FormSetsMixin, ModelFormSetsMixin, 4 EnhancedModelFormSet, ) 5 from regressiontests.generic_views.models import Author, Article 6 7 8 class FormSetsViewTests(TestCase): 9 urls = 'regressiontests.generic_views.urls' 10 11 def setUp(self): 12 self.data = { 13 'form-TOTAL_FORMS': u'3', 14 'form-INITIAL_FORMS': u'0', 15 'form-MAX_NUM_FORMS': u'', 16 'form-0-title': u'', 17 'form-0-pubdate': u'', 18 'form-1-title': u'', 19 'form-1-pubdate': u'', 20 'form-2-title': u'', 21 'form-2-pubdate': u'', 22 23 'form-2-TOTAL_FORMS': u'3', 24 'form-2-INITIAL_FORMS': u'0', 25 'form-2-MAX_NUM_FORMS': u'', 26 'form-2-0-name': u'', 27 'form-2-0-slug': u'', 28 'form-2-1-name': u'', 29 'form-2-1-slug': u'', 30 'form-2-2-name': u'', 31 'form-2-2-slug': u'', 32 } 33 34 def test_get(self): 35 response = self.client.get('/edit/formsets/') 36 self.assertEqual(response.status_code, 200) 37 38 def test_empty_post(self): 39 response = self.client.post('/edit/formsets/', self.data) 40 self.assertEqual(response.status_code, 302) 41 42 def test_valid(self): 43 self.data.update({ 44 'form-0-title': u'first title', 45 'form-0-pubdate': u'2011-01-13', 46 'form-1-title': u'second title', 47 'form-1-pubdate': u'2011-01-13', 48 'form-2-0-name': u'this is my name', 49 'form-2-0-slug': u'this-is-my-name', 50 }) 51 response = self.client.post('/edit/formsets/', self.data) 52 self.assertEqual(response.status_code, 302) 53 54 def test_invalid(self): 55 self.data.update({ 56 'form-0-title': u'first title', 57 'form-0-pubdate': u'', 58 }) 59 response = self.client.post('/edit/formsets/', self.data) 60 self.assertEqual(response.status_code, 200) 61 self.assertContains(response, 'ERROR') 62 63 64 class ModelFormSetsTests(TestCase): 65 def test_no_model_no_form_class(self): 66 formset = EnhancedModelFormSet() 67 self.assertRaises(ImproperlyConfigured, formset.get_model) 68 69 70 class ModelFormSetsViewTests(TestCase): 71 urls = 'regressiontests.generic_views.urls' 72 73 def setUp(self): 74 self.data = { 75 'form-TOTAL_FORMS': u'3', 76 'form-INITIAL_FORMS': u'0', 77 'form-MAX_NUM_FORMS': u'', 78 'form-0-title': u'', 79 'form-0-pubdate': u'', 80 'form-1-title': u'', 81 'form-1-pubdate': u'', 82 'form-2-title': u'', 83 'form-2-pubdate': u'', 84 85 'form-2-TOTAL_FORMS': u'3', 86 'form-2-INITIAL_FORMS': u'0', 87 'form-2-MAX_NUM_FORMS': u'', 88 'form-2-0-name': u'', 89 'form-2-1-name': u'', 90 'form-2-2-name': u'', 91 } 92 93 def test_get(self): 94 response = self.client.get('/edit/modelformsets/') 95 self.assertEqual(response.status_code, 200) 96 97 def test_empty_post(self): 98 response = self.client.post('/edit/modelformsets/', self.data) 99 self.assertEqual(response.status_code, 302) 100 101 def test_valid(self): 102 self.data.update({ 103 'form-0-title': u'first title', 104 'form-0-pubdate': u'2011-01-13', 105 'form-1-title': u'second title', 106 'form-1-pubdate': u'2011-01-13', 107 'form-2-0-name': u'this is my name', 108 'form-2-0-slug': u'this-is-my-name', 109 }) 110 response = self.client.post('/edit/modelformsets/', self.data) 111 self.assertEqual(Article.objects.count(), 2) 112 self.assertEqual(Author.objects.count(), 1) 113 self.assertEqual(response.status_code, 302) 114 115 def test_invalid(self): 116 self.data.update({ 117 'form-0-title': u'first title', 118 'form-0-pubdate': u'', 119 }) 120 response = self.client.post('/edit/modelformsets/', self.data) 121 self.assertEqual(Article.objects.count(), 0) 122 self.assertEqual(Author.objects.count(), 0) 123 self.assertEqual(response.status_code, 200) 124 self.assertContains(response, 'ERROR') 125 126 127 class InlineFormSetsViewTests(TestCase): 128 urls = 'regressiontests.generic_views.urls' 129 130 def setUp(self): 131 self.formsetmgmt = { 132 'article_set-TOTAL_FORMS': u'3', 133 'article_set-INITIAL_FORMS': u'0', 134 'article_set-MAX_NUM_FORMS': u'', 135 } 136 self.formsetdata = { 137 'article_set-0-title': u'title1', 138 'article_set-0-pubdate': u'2011-01-26', 139 'article_set-1-title': u'title2', 140 'article_set-1-pubdate': u'2011-01-26', 141 'article_set-2-title': u'title3', 142 'article_set-2-pubdate': u'2011-01-26', 143 } 144 self.formdata = { 145 'name': u'this is my name', 146 'slug': u'this-is-my-name', 147 } 148 149 def test_get(self): 150 response = self.client.get('/edit/inlineformsets/') 151 self.assertEqual(response.status_code, 200) 152 153 def test_empty_post(self): 154 data = { 155 'article_set-TOTAL_FORMS': u'3', 156 'article_set-INITIAL_FORMS': u'0', 157 'article_set-MAX_NUM_FORMS': u'', 158 'article_set-0-title': u'', 159 'article_set-0-pubdate': u'', 160 'article_set-1-title': u'', 161 'article_set-1-pubdate': u'', 162 'article_set-2-title': u'', 163 'article_set-2-pubdate': u'', 164 'name': u'', 165 } 166 response = self.client.post('/edit/inlineformsets/', data) 167 self.assertEqual(response.status_code, 200) 168 169 def test_valid(self): 170 data = self.formdata 171 data.update(self.formsetdata) 172 data.update(self.formsetmgmt) 173 response = self.client.post('/edit/inlineformsets/', data) 174 self.assertEqual(Article.objects.count(), 3) 175 self.assertEqual(Author.objects.count(), 1) 176 self.assertEqual(response.status_code, 302) 177 178 def test_no_form(self): 179 data = self.formsetdata 180 data.update(self.formsetmgmt) 181 response = self.client.post('/edit/inlineformsets/', data) 182 self.assertEqual(Article.objects.count(), 0) 183 self.assertEqual(Author.objects.count(), 0) 184 self.assertEqual(response.status_code, 200) 185 self.assertContains(response, 'ERROR') 186 187 def test_no_formset(self): 188 data = self.formdata 189 data.update(self.formsetmgmt) 190 response = self.client.post('/edit/inlineformsets/', data) 191 self.assertEqual(Article.objects.count(), 0) 192 self.assertEqual(Author.objects.count(), 1) 193 self.assertEqual(response.status_code, 302) -
tests/regressiontests/generic_views/models.py
diff --git a/tests/regressiontests/generic_views/models.py b/tests/regressiontests/generic_views/models.py index 5445e24..3a0d9ab 100644
a b class Book(models.Model): 41 41 class Page(models.Model): 42 42 content = models.TextField() 43 43 template = models.CharField(max_length=300) 44 45 46 class Article(models.Model): 47 title = models.CharField(max_length=100) 48 pubdate = models.DateField() 49 author = models.ForeignKey(Author, blank=True, null=True) -
new file tests/regressiontests/generic_views/templates/authors_articles.html
diff --git a/tests/regressiontests/generic_views/templates/authors_articles.html b/tests/regressiontests/generic_views/templates/authors_articles.html new file mode 100644 index 0000000..7c9eca3
- + 1 {% for formset in formsets %} 2 {% if formset.errors %} 3 ERROR 4 {{formset.errors}} 5 {% endif %} 6 {% for form in formset %} 7 {{form.as_p}} 8 {% endfor %} 9 {{formset.management_form}} 10 {% endfor %} -
tests/regressiontests/generic_views/tests.py
diff --git a/tests/regressiontests/generic_views/tests.py b/tests/regressiontests/generic_views/tests.py index a4010aa..0ab240c 100644
a b from regressiontests.generic_views.dates import ArchiveIndexViewTests, YearArchi 3 3 from regressiontests.generic_views.detail import DetailViewTest 4 4 from regressiontests.generic_views.edit import ModelFormMixinTests, CreateViewTests, UpdateViewTests, DeleteViewTests 5 5 from regressiontests.generic_views.list import ListViewTests 6 from regressiontests.generic_views.formsets import FormSetsViewTests, ModelFormSetsTests, ModelFormSetsViewTests, InlineFormSetsViewTests -
tests/regressiontests/generic_views/urls.py
diff --git a/tests/regressiontests/generic_views/urls.py b/tests/regressiontests/generic_views/urls.py index 067c1f6..8ec94cf 100644
a b urlpatterns = patterns('', 208 208 views.BookDetail.as_view(allow_future=True)), 209 209 (r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/nopk/$', 210 210 views.BookDetail.as_view()), 211 212 211 (r'^dates/books/(?P<year>\d{4})/(?P<month>[a-z]{3})/(?P<day>\d{1,2})/byslug/(?P<slug>[\w-]+)/$', 213 212 views.BookDetail.as_view()), 214 213 214 # FormSets 215 (r'^edit/formsets/$', 216 views.AuthorsArticlesView.as_view()), 217 (r'^edit/modelformsets/$', 218 views.AuthorsArticlesModelsView.as_view()), 219 (r'^edit/inlineformsets/$', 220 views.AuthorsInlinesView.as_view()), 221 215 222 # Useful for testing redirects 216 223 (r'^accounts/login/$', 'django.contrib.auth.views.login') 217 224 ) -
tests/regressiontests/generic_views/views.py
diff --git a/tests/regressiontests/generic_views/views.py b/tests/regressiontests/generic_views/views.py index 0c8fd49..69ea1ac 100644
a b from django.utils.decorators import method_decorator 5 5 from django.views import generic 6 6 7 7 from regressiontests.generic_views.models import Artist, Author, Book, Page 8 from regressiontests.generic_views.forms import AuthorForm 9 8 from regressiontests.generic_views.forms import (AuthorForm, 9 ArticleEnhancedFormSet, AuthorEnhancedFormSet, 10 ArticleEnhancedModelFormSet, AuthorEnhancedModelFormSet, 11 ArticleEnhancedInlineFormSet, ) 10 12 11 13 class CustomTemplateView(generic.TemplateView): 12 14 template_name = 'generic_views/about.html' … … class BookDetail(BookConfig, generic.DateDetailView): 177 179 class AuthorGetQuerySetFormView(generic.edit.ModelFormMixin): 178 180 def get_queryset(self): 179 181 return Author.objects.all() 182 183 184 class AuthorsArticlesView(generic.FormSetsView): 185 formsets = [ArticleEnhancedFormSet, AuthorEnhancedFormSet, ] 186 template_name = 'authors_articles.html' 187 success_url = '/list/authors/' 188 189 190 class AuthorsArticlesModelsView(generic.ModelFormSetsView): 191 formsets = [ArticleEnhancedModelFormSet, AuthorEnhancedModelFormSet, ] 192 template_name = 'authors_articles.html' 193 success_url = '/list/authors/' 194 195 196 class AuthorsInlinesView(generic.InlineFormSetsView): 197 formsets = [ArticleEnhancedInlineFormSet, ] 198 template_name = 'authors_articles.html' 199 success_url = '/list/authors/' 200 model = Author