Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

newforms-admin: Changed the 'fields' option on ModelAdmin to 'fieldse…

…ts'. 'fields' is still valid, but should be a list of field names instead. Also, added some new hooks that were needed to support the change.

git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@6080 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit b5aa61a325136d6484ccbfc8a0efd98e8aaa964a 1 parent c05527d
Joseph Kocherhans authored September 10, 2007
156  django/contrib/admin/options.py
@@ -36,28 +36,41 @@ def unquote(s):
36 36
             myappend('_' + item)
37 37
     return "".join(res)
38 38
 
  39
+def flatten_fieldsets(fieldsets):
  40
+    """Returns a list of field names from an admin fieldsets structure."""
  41
+    field_names = []
  42
+    for name, opts in fieldsets:
  43
+        for field in opts['fields']:
  44
+            # type checking feels dirty, but it seems like the best way here
  45
+            if type(field) == tuple:
  46
+                field_names.extend(field)
  47
+            else:
  48
+                field_names.append(field)
  49
+    return field_names
  50
+
39 51
 class AdminForm(object):
40 52
     def __init__(self, form, fieldsets, prepopulated_fields):
41 53
         self.form, self.fieldsets = form, fieldsets
42 54
         self.prepopulated_fields = [{'field': form[field_name], 'dependencies': [form[f] for f in dependencies]} for field_name, dependencies in prepopulated_fields.items()]
43 55
 
44 56
     def __iter__(self):
45  
-        for fieldset in self.fieldsets:
46  
-            yield BoundFieldset(self.form, fieldset)
  57
+        for name, options in self.fieldsets:
  58
+            yield Fieldset(self.form, name, **options)
47 59
 
48 60
     def first_field(self):
49 61
         for bf in self.form:
50 62
             return bf
51  
-            
  63
+
52 64
     def _media(self):
53 65
         media = self.form.media
54  
-        for fs in self.fieldsets:
  66
+        for fs in self:
55 67
             media = media + fs.media
56 68
         return media
57 69
     media = property(_media)
58 70
 
59 71
 class Fieldset(object):
60  
-    def __init__(self, name=None, fields=(), classes=(), description=None):
  72
+    def __init__(self, form, name=None, fields=(), classes=(), description=None):
  73
+        self.form = form
61 74
         self.name, self.fields = name, fields
62 75
         self.classes = u' '.join(classes)
63 76
         self.description = description
@@ -68,16 +81,12 @@ def _media(self):
68 81
             return forms.Media(js=['%sjs/admin/CollapsedFieldsets.js' % settings.ADMIN_MEDIA_PREFIX])
69 82
         return forms.Media()
70 83
     media = property(_media)
71  
-    
72  
-class BoundFieldset(object):
73  
-    def __init__(self, form, fieldset):
74  
-        self.form, self.fieldset = form, fieldset
75 84
 
76 85
     def __iter__(self):
77  
-        for field in self.fieldset.fields:
78  
-            yield BoundFieldline(self.form, field)
  86
+        for field in self.fields:
  87
+            yield Fieldline(self.form, field)
79 88
 
80  
-class BoundFieldline(object):
  89
+class Fieldline(object):
81 90
     def __init__(self, form, field):
82 91
         self.form = form # A django.forms.Form instance
83 92
         if isinstance(field, basestring):
@@ -87,12 +96,12 @@ def __init__(self, form, field):
87 96
 
88 97
     def __iter__(self):
89 98
         for i, field in enumerate(self.fields):
90  
-            yield BoundField(self.form, field, is_first=(i == 0))
  99
+            yield AdminField(self.form, field, is_first=(i == 0))
91 100
 
92 101
     def errors(self):
93 102
         return u'\n'.join([self.form[f].errors.as_ul() for f in self.fields])
94 103
 
95  
-class BoundField(object):
  104
+class AdminField(object):
96 105
     def __init__(self, form, field, is_first):
97 106
         self.field = form[field] # A django.forms.BoundField instance
98 107
         self.is_first = is_first # Whether this field is first on the line
@@ -116,10 +125,17 @@ class BaseModelAdmin(object):
116 125
     """Functionality common to both ModelAdmin and InlineAdmin."""
117 126
     raw_id_fields = ()
118 127
     fields = None
  128
+    fieldsets = None
119 129
     filter_vertical = ()
120 130
     filter_horizontal = ()
121 131
     prepopulated_fields = {}
122 132
 
  133
+    def __init__(self):
  134
+        # TODO: This should really go in django.core.validation, but validation
  135
+        # doesn't work on ModelAdmin classes yet.
  136
+        if self.fieldsets and self.fields:
  137
+            raise ImproperlyConfigured('Both fieldsets and fields is specified for %s.' % self.model)
  138
+
123 139
     def formfield_for_dbfield(self, db_field, **kwargs):
124 140
         """
125 141
         Hook for specifying the form Field instance for a given database Field
@@ -163,21 +179,22 @@ def formfield_for_dbfield(self, db_field, **kwargs):
163 179
         # For any other type of field, just call its formfield() method.
164 180
         return db_field.formfield(**kwargs)
165 181
 
166  
-    def fieldsets(self, request):
167  
-        """
168  
-        Generator that yields Fieldset objects for use on add and change admin
169  
-        form pages.
  182
+    def _fieldsets(self, request):
  183
+        if self.fieldsets:
  184
+            return self.fieldsets
  185
+        if self.fields:
  186
+            return [(None, {'fields': self.fields})]
  187
+        # TODO: switch this to pull from the form, not the model
  188
+        fields = [f.name for f in self.opts.fields + self.opts.many_to_many if f.editable and not isinstance(f, models.AutoField)]
  189
+        return [(None, {'fields': fields})]
170 190
 
171  
-        This default implementation looks at self.fields, but subclasses can
172  
-        override this implementation and do something special based on the
173  
-        given HttpRequest object.
174  
-        """
175  
-        if self.fields is None:
176  
-            default_fields = [f.name for f in self.opts.fields + self.opts.many_to_many if f.editable and not isinstance(f, models.AutoField)]
177  
-            yield Fieldset(fields=default_fields)
178  
-        else:
179  
-            for name, options in self.fields:
180  
-                yield Fieldset(name, options['fields'], classes=options.get('classes', '').split(' '), description=options.get('description'))
  191
+    def fieldsets_add(self, request):
  192
+        "Hook for specifying fieldsets for the add form."
  193
+        return list(self._fieldsets(request))
  194
+
  195
+    def fieldsets_change(self, request, obj):
  196
+        "Hook for specifying fieldsets for the change form."
  197
+        return list(self._fieldsets(request))
181 198
 
182 199
 class ModelAdmin(BaseModelAdmin):
183 200
     "Encapsulates all admin options and functionality for a given model."
@@ -203,6 +220,7 @@ def __init__(self, model, admin_site):
203 220
         for inline_class in self.inlines:
204 221
             inline_instance = inline_class(self.model, self.admin_site)
205 222
             self.inline_instances.append(inline_instance)
  223
+        super(ModelAdmin, self).__init__()
206 224
 
207 225
     def __call__(self, request, url):
208 226
         # Check that LogEntry, ContentType and the auth context processor are installed.
@@ -243,14 +261,6 @@ def _media(self):
243 261
         return forms.Media(js=['%s%s' % (settings.ADMIN_MEDIA_PREFIX, url) for url in js])
244 262
     media = property(_media)
245 263
     
246  
-    def fieldsets_add(self, request):
247  
-        "Hook for specifying Fieldsets for the add form."
248  
-        return list(self.fieldsets(request))
249  
-
250  
-    def fieldsets_change(self, request, obj):
251  
-        "Hook for specifying Fieldsets for the change form."
252  
-        return list(self.fieldsets(request))
253  
-
254 264
     def has_add_permission(self, request):
255 265
         "Returns True if the given request has permission to add an object."
256 266
         opts = self.opts
@@ -413,19 +423,20 @@ def add_view(self, request, form_url=''):
413 423
             # Object list will give 'Permission Denied', so go back to admin home
414 424
             post_url = '../../../'
415 425
 
416  
-        ModelForm = forms.form_for_model(model, formfield_callback=self.formfield_for_dbfield)
  426
+        fields = flatten_fieldsets(self.fieldsets_add(request))
  427
+        ModelForm = forms.form_for_model(model, fields=fields, formfield_callback=self.formfield_for_dbfield)
417 428
 
418 429
         inline_formsets = []
419 430
         if request.method == 'POST':
420 431
             form = ModelForm(request.POST, request.FILES)
421  
-            for FormSet in self.get_inline_formsets():
  432
+            for FormSet in self.formsets_add(request):
422 433
                 inline_formset = FormSet(data=request.POST, files=request.FILES)
423 434
                 inline_formsets.append(inline_formset)
424 435
             if all_valid(inline_formsets) and form.is_valid():
425 436
                 return self.save_add(request, model, form, inline_formsets, '../%s/')
426 437
         else:
427 438
             form = ModelForm(initial=request.GET)
428  
-            for FormSet in self.get_inline_formsets():
  439
+            for FormSet in self.formsets_add(request):
429 440
                 inline_formset = FormSet()
430 441
                 inline_formsets.append(inline_formset)
431 442
 
@@ -436,7 +447,7 @@ def add_view(self, request, form_url=''):
436 447
 
437 448
         inline_admin_formsets = []
438 449
         for inline, formset in zip(self.inline_instances, inline_formsets):
439  
-            fieldsets = list(inline.fieldsets(request))
  450
+            fieldsets = list(inline.fieldsets_add(request))
440 451
             inline_admin_formset = InlineAdminFormSet(inline, formset, fieldsets)
441 452
             inline_admin_formsets.append(inline_admin_formset)
442 453
 
@@ -474,12 +485,13 @@ def change_view(self, request, object_id):
474 485
         if request.POST and request.POST.has_key("_saveasnew"):
475 486
             return self.add_view(request, form_url='../../add/')
476 487
 
477  
-        ModelForm = forms.form_for_instance(obj, formfield_callback=self.formfield_for_dbfield)
  488
+        fields = flatten_fieldsets(self.fieldsets_change(request, obj))
  489
+        ModelForm = forms.form_for_instance(obj, fields=fields, formfield_callback=self.formfield_for_dbfield)
478 490
 
479 491
         inline_formsets = []
480 492
         if request.method == 'POST':
481 493
             form = ModelForm(request.POST, request.FILES)
482  
-            for FormSet in self.get_inline_formsets():
  494
+            for FormSet in self.formsets_change(request, obj):
483 495
                 inline_formset = FormSet(obj, request.POST, request.FILES)
484 496
                 inline_formsets.append(inline_formset)
485 497
 
@@ -487,7 +499,7 @@ def change_view(self, request, object_id):
487 499
                 return self.save_change(request, model, form, inline_formsets)
488 500
         else:
489 501
             form = ModelForm()
490  
-            for FormSet in self.get_inline_formsets():
  502
+            for FormSet in self.formsets_change(request, obj):
491 503
                 inline_formset = FormSet(obj)
492 504
                 inline_formsets.append(inline_formset)
493 505
 
@@ -505,14 +517,14 @@ def change_view(self, request, object_id):
505 517
                 #orig_list = func()
506 518
                 #oldform.order_objects.extend(orig_list)
507 519
                 
508  
-        adminForm = AdminForm(form, self.fieldsets_change(request, obj), self.prepopulated_fields)        
  520
+        adminForm = AdminForm(form, self.fieldsets_change(request, obj), self.prepopulated_fields)
509 521
         media = self.media + adminForm.media
510 522
         for fs in inline_formsets:
511 523
             media = media + fs.media
512 524
 
513 525
         inline_admin_formsets = []
514 526
         for inline, formset in zip(self.inline_instances, inline_formsets):
515  
-            fieldsets = list(inline.fieldsets(request))
  527
+            fieldsets = list(inline.fieldsets_change(request, obj))
516 528
             inline_admin_formset = InlineAdminFormSet(inline, formset, fieldsets)
517 529
             inline_admin_formsets.append(inline_admin_formset)
518 530
 
@@ -626,9 +638,13 @@ def history_view(self, request, object_id):
626 638
         ]
627 639
         return render_to_response(template_list, extra_context, context_instance=template.RequestContext(request))
628 640
 
629  
-    def get_inline_formsets(self):
  641
+    def formsets_add(self, request):
  642
+        for inline in self.inline_instances:
  643
+            yield inline.formset_add(request)
  644
+
  645
+    def formsets_change(self, request, obj):
630 646
         for inline in self.inline_instances:
631  
-            yield inline.formset_class
  647
+            yield inline.formset_change(request, obj)
632 648
 
633 649
 class InlineModelAdmin(BaseModelAdmin):
634 650
     """
@@ -648,23 +664,25 @@ def __init__(self, parent_model, admin_site):
648 664
         self.admin_site = admin_site
649 665
         self.parent_model = parent_model
650 666
         self.opts = self.model._meta
651  
-        # TODO: pass a fields arg into forms.inline_formset if/when we have one
652  
-        self.formset_class = forms.inline_formset(parent_model, self.model, fk_name=self.fk_name, formfield_callback=self.formfield_for_dbfield, extra=self.extra)
653  
-
654  
-    def fieldsets(self, request):
655  
-        """
656  
-        Generator that yields Fieldset objects for use on add and change admin
657  
-        form pages.
658  
-
659  
-        This default implementation looks at self.fields, but subclasses can
660  
-        override this implementation and do something special based on the
661  
-        given HttpRequest object.
662  
-        """
663  
-        if self.fields is None:
664  
-            yield Fieldset(fields=self.formset_class.form_class.base_fields)
665  
-        else:
666  
-            for name, options in self.fields:
667  
-                yield Fieldset(name, options['fields'], classes=options.get('classes', '').split(' '), description=options.get('description'))
  667
+        super(InlineModelAdmin, self).__init__()
  668
+
  669
+    def formset_add(self, request):
  670
+        """Returns an InlineFormSet class for use in admin add views."""
  671
+        fields = flatten_fieldsets(self.fieldsets_add(request))
  672
+        return forms.inline_formset(self.parent_model, self.model, fk_name=self.fk_name, fields=fields, formfield_callback=self.formfield_for_dbfield, extra=self.extra)
  673
+
  674
+    def formset_change(self, request, obj):
  675
+        """Returns an InlineFormSet class for use in admin change views."""
  676
+        fields = flatten_fieldsets(self.fieldsets_change(request, obj))
  677
+        return forms.inline_formset(self.parent_model, self.model, fk_name=self.fk_name, fields=fields, formfield_callback=self.formfield_for_dbfield, extra=self.extra)
  678
+
  679
+    def _fieldsets(self, request):
  680
+        if self.fieldsets:
  681
+            return self.fieldsets
  682
+        if self.fields:
  683
+            return [(None, {'fields': self.fields})]
  684
+        fields = [f for f in self.formset_class(request).form_class.base_fields.keys()]
  685
+        return [(None, {'fields': fields})]
668 686
 
669 687
 class StackedInline(InlineModelAdmin):
670 688
     template = 'admin/edit_inline_stacked.html'
@@ -694,8 +712,8 @@ def __iter__(self):
694 712
             yield InlineAdminForm(self.formset, form, self.fieldsets, self.opts.prepopulated_fields, None)
695 713
 
696 714
     def fields(self):
697  
-        # TODO: this needs to respect the field order of self.fieldsets
698  
-        return self.formset.form_class.base_fields.values()
  715
+        for field_name in flatten_fieldsets(self.fieldsets):
  716
+            yield self.formset.form_class.base_fields[field_name]
699 717
 
700 718
 class InlineAdminForm(AdminForm):
701 719
     """
@@ -707,12 +725,12 @@ def __init__(self, formset, form, fieldsets, prepopulated_fields, original):
707 725
         super(InlineAdminForm, self).__init__(form, fieldsets, prepopulated_fields)
708 726
 
709 727
     def pk_field(self):
710  
-        return BoundField(self.form, self.formset._pk_field_name, False)
  728
+        return AdminField(self.form, self.formset._pk_field_name, False)
711 729
 
712 730
     def deletion_field(self):
713 731
         from django.newforms.formsets import DELETION_FIELD_NAME
714  
-        return BoundField(self.form, DELETION_FIELD_NAME, False)
  732
+        return AdminField(self.form, DELETION_FIELD_NAME, False)
715 733
 
716 734
     def ordering_field(self):
717 735
         from django.newforms.formsets import ORDERING_FIELD_NAME
718  
-        return BoundField(self.form, ORDERING_FIELD_NAME, False)
  736
+        return AdminField(self.form, ORDERING_FIELD_NAME, False)
10  django/contrib/admin/templates/admin/change_form.html
@@ -40,11 +40,11 @@
40 40
     </p>
41 41
 {% endif %}
42 42
 
43  
-{% for bfset in adminform %}
44  
-    <fieldset class="module aligned {{ bfset.fieldset.classes }}">
45  
-    {% if bfset.fieldset.name %}<h2>{{ bfset.fieldset.name }}</h2>{% endif %}
46  
-    {% if bfset.fieldset.description %}<div class="description">{{ bfset.fieldset.description }}</div>{% endif %}
47  
-    {% for line in bfset %}
  43
+{% for fieldset in adminform %}
  44
+    <fieldset class="module aligned {{ fieldset.classes }}">
  45
+    {% if fieldset.name %}<h2>{{ fieldset.name }}</h2>{% endif %}
  46
+    {% if fieldset.description %}<div class="description">{{ fieldset.description }}</div>{% endif %}
  47
+    {% for line in fieldset %}
48 48
         <div class="form-row{% if line.errors %} errors{% endif %}">
49 49
         {{ line.errors }}
50 50
         {% for field in line %}
4  django/contrib/admin/templates/admin/edit_inline_tabular.html
@@ -25,8 +25,8 @@
25 25
         <tr class="{% cycle row1,row2 %}">
26 26
         {{ inline_admin_form.pk_field.field }}
27 27
         {% if inline_admin_formset.formset.deletable %}<td>{{ inline_admin_form.deletion_field.field }}</td>{% endif %}
28  
-        {% for bfset in inline_admin_form %}
29  
-          {% for line in bfset %}
  28
+        {% for fieldset in inline_admin_form %}
  29
+          {% for line in fieldset %}
30 30
             {% for field in line %}
31 31
               <td>{{ field.field }}</td>
32 32
             {% endfor %}

0 notes on commit b5aa61a

Please sign in to comment.
Something went wrong with that request. Please try again.