Changeset 5473
- Timestamp:
- 06/14/07 22:40:45 (2 years ago)
- Files:
-
- django/branches/newforms-admin/django/contrib/admin/__init__.py (modified) (1 diff)
- django/branches/newforms-admin/django/contrib/admin/options.py (modified) (14 diffs)
- django/branches/newforms-admin/django/contrib/admin/templates/admin/change_form.html (modified) (1 diff)
- django/branches/newforms-admin/django/contrib/admin/templates/admin/edit_inline_stacked.html (modified) (2 diffs)
- django/branches/newforms-admin/django/contrib/admin/templates/admin/edit_inline_tabular.html (modified) (2 diffs)
- django/branches/newforms-admin/django/contrib/admin/templatetags/admin_modify.py (modified) (1 diff)
- django/branches/newforms-admin/django/newforms/formsets.py (modified) (6 diffs)
- django/branches/newforms-admin/django/newforms/models.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/newforms-admin/django/contrib/admin/__init__.py
r4581 r5473 1 1 from django.contrib.admin.options import ModelAdmin 2 from django.contrib.admin.options import StackedInline, TabularInline 2 3 from django.contrib.admin.sites import AdminSite, site django/branches/newforms-admin/django/contrib/admin/options.py
r4941 r5473 1 1 from django import oldforms, template 2 2 from django import newforms as forms 3 from django.newforms.formsets import all_valid 4 from django.newforms.models import inline_formset 3 5 from django.contrib.admin import widgets 4 6 from django.core.exceptions import ImproperlyConfigured, PermissionDenied … … 94 96 attrs = classes and {'class': ' '.join(classes)} or {} 95 97 return self.field.label_tag(contents=contents, attrs=attrs) 98 99 class InlineOptions(object): 100 """ 101 Options for inline editing of ``model`` instances. 102 103 Provide ``name`` to specify the attribute name of the ``ForeignKey`` from 104 ``model`` to its parent. This is required if ``model`` has more than one 105 ``ForeignKey`` to its parent. 106 """ 107 def __init__(self, model, name=None, extra=3, fields=None, template=None, formfield_callback=lambda f: f.formfield()): 108 self.model = model 109 self.name = name 110 self.extra = extra 111 self.fields = fields 112 self.template = template or self.default_template 113 self.verbose_name = model._meta.verbose_name 114 self.verbose_name_plural = model._meta.verbose_name_plural 115 self.prepopulated_fields = {} 116 self.formfield_callback = formfield_callback 117 118 class StackedInline(InlineOptions): 119 default_template = 'admin/edit_inline_stacked.html' 120 121 class TabularInline(InlineOptions): 122 default_template = 'admin/edit_inline_tabular.html' 123 124 class BoundInline(object): 125 def __init__(self, opts, formset): 126 self.opts = opts 127 self.formset = formset 128 129 def __iter__(self): 130 for form, original in zip(self.formset.change_forms, self.formset.get_inline_objects()): 131 yield BoundInlineObject(form, original, self.opts) 132 for form in self.formset.add_forms: 133 yield BoundInlineObject(form, None, self.opts) 134 135 def fields(self): 136 # HACK: each form instance has some extra fields. Getting those fields 137 # from the form class will take some rearranging. Get them from the 138 # first form instance for now. 139 return list(self.formset.forms[0]) 140 141 def verbose_name(self): 142 return self.opts.verbose_name 143 144 def verbose_name_plural(self): 145 return self.opts.verbose_name_plural 146 147 class BoundInlineObject(object): 148 def __init__(self, form, original, opts): 149 self.opts = opts 150 self.base_form = form 151 self.form = AdminForm(form, self.fieldsets(), opts.prepopulated_fields) 152 self.original = original 153 154 def fieldsets(self): 155 """ 156 Generator that yields Fieldset objects for use on add and change admin 157 form pages. 158 159 This default implementation looks at self.fields, but subclasses can 160 override this implementation and do something special based on the 161 given HttpRequest object. 162 """ 163 if self.opts.fields is None: 164 default_fields = [f for f in self.base_form.fields] 165 yield Fieldset(fields=default_fields) 166 else: 167 for name, options in self.opts.fields: 168 yield Fieldset(name, options['fields'], classes=options.get('classes', '').split(' '), description=options.get('description')) 96 169 97 170 class ModelAdmin(object): … … 293 366 return self.queryset(request) 294 367 295 def save_add(self, request, model, form, post_url_continue):368 def save_add(self, request, model, form, formsets, post_url_continue): 296 369 """ 297 370 Saves the object in the "add" stage and returns an HttpResponseRedirect. … … 303 376 opts = model._meta 304 377 new_object = form.save(commit=True) 378 379 if formsets: 380 for formset in formsets: 381 # HACK: it seems like the parent obejct should be passed into 382 # a method of something, not just set as an attribute 383 formset.instance = new_object 384 formset.save() 385 305 386 pk_value = new_object._get_pk_val() 306 387 LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, pk_value, str(new_object), ADDITION) … … 332 413 return HttpResponseRedirect(post_url) 333 414 334 def save_change(self, request, model, form ):415 def save_change(self, request, model, form, formsets=None): 335 416 """ 336 417 Saves the object in the "change" stage and returns an HttpResponseRedirect. 337 418 338 419 `form` is a bound Form instance that's verified to be valid. 420 421 `formsets` is a sequence of InlineFormSet instances that are verified to be valid. 339 422 """ 340 423 from django.contrib.admin.models import LogEntry, CHANGE … … 343 426 new_object = form.save(commit=True) 344 427 pk_value = new_object._get_pk_val() 428 429 if formsets: 430 for formset in formsets: 431 formset.save() 345 432 346 433 # Construct the change message. TODO: Temporarily commented-out, … … 395 482 ModelForm = forms.form_for_model(model, formfield_callback=self.formfield_for_dbfield) 396 483 484 inline_formsets = [] 397 485 if request.POST: 398 486 new_data = request.POST.copy() … … 400 488 new_data.update(request.FILES) 401 489 form = ModelForm(new_data) 402 if form.is_valid(): 403 return self.save_add(request, model, form, '../%s/') 490 for FormSet in self.get_inline_formsets(): 491 inline_formset = FormSet(data=new_data) 492 inline_formsets.append(inline_formset) 493 if form.is_valid() and all_valid(inline_formsets): 494 return self.save_add(request, model, form, inline_formsets, '../%s/') 404 495 else: 405 496 form = ModelForm(initial=request.GET) 497 for FormSet in self.get_inline_formsets(): 498 inline_formset = FormSet() 499 inline_formsets.append(inline_formset) 406 500 407 501 c = template.RequestContext(request, { … … 411 505 'show_delete': False, 412 506 'javascript_imports': self.javascript_add(request), 507 'bound_inlines': [BoundInline(i, fs) for i, fs in zip(self.inlines, inline_formsets)], 413 508 }) 414 509 return render_change_form(self, model, model.AddManipulator(), c, add=True) … … 440 535 ModelForm = forms.form_for_instance(obj, formfield_callback=self.formfield_for_dbfield) 441 536 537 inline_formsets = [] 442 538 if request.POST: 443 539 new_data = request.POST.copy() … … 445 541 new_data.update(request.FILES) 446 542 form = ModelForm(new_data) 447 448 if form.is_valid(): 449 return self.save_change(request, model, form) 543 for FormSet in self.get_inline_formsets(): 544 inline_formset = FormSet(obj, new_data) 545 inline_formsets.append(inline_formset) 546 547 if form.is_valid() and all_valid(inline_formsets): 548 return self.save_change(request, model, form, inline_formsets) 450 549 else: 451 550 form = ModelForm() 551 for FormSet in self.get_inline_formsets(): 552 inline_formset = FormSet(obj) 553 inline_formsets.append(inline_formset) 452 554 453 555 ## Populate the FormWrapper. … … 464 566 #orig_list = func() 465 567 #oldform.order_objects.extend(orig_list) 466 467 568 c = template.RequestContext(request, { 468 569 'title': _('Change %s') % opts.verbose_name, … … 472 573 'is_popup': request.REQUEST.has_key('_popup'), 473 574 'javascript_imports': self.javascript_change(request, obj), 575 'bound_inlines': [BoundInline(i, fs) for i, fs in zip(self.inlines, inline_formsets)], 474 576 }) 475 577 return render_change_form(self, model, model.ChangeManipulator(object_id), c, change=True) … … 573 675 ] 574 676 return render_to_response(template_list, extra_context, context_instance=template.RequestContext(request)) 677 678 def get_inline_formsets(self): 679 inline_formset_classes = [] 680 for opts in self.inlines: 681 inline = inline_formset(self.model, opts.model, formfield_callback=self.formfield_for_dbfield, fields=opts.fields, extra=opts.extra) 682 inline_formset_classes.append(inline) 683 return inline_formset_classes django/branches/newforms-admin/django/contrib/admin/templates/admin/change_form.html
r4569 r5473 64 64 {% block after_field_sets %}{% endblock %} 65 65 66 {% for bound_inline in bound_inlines %} 67 {% render_inline bound_inline %} 68 {% endfor %} 69 66 70 {% block after_related_objects %}{% endblock %} 67 71 django/branches/newforms-admin/django/contrib/admin/templates/admin/edit_inline_stacked.html
r3349 r5473 1 {{ bound_inline.formset.management_form }} 2 {% for bound_inline_object in bound_inline %} 3 <fieldset class="module aligned {{ bfset.fieldset.classes }}"> 4 <h2>{{ bound_inline.verbose_name|title }} #{{ forloop.counter }}</h2> 5 {% for bfset in bound_inline_object.form %} 6 <fieldset class="module aligned {{ bfset.fieldset.classes }}"> 7 {% if bfset.fieldset.name %}<h2>{{ bfset.fieldset.name }}</h2>{% endif %} 8 {% if bfset.fieldset.description %}<div class="description">{{ bfset.fieldset.description }}</div>{% endif %} 9 {% for line in bfset %} 10 <div class="form-row{% if line.errors %} errors{% endif %}"> 11 {{ line.errors }} 12 {% for field in line %} 13 {% if field.is_checkbox %} 14 {{ field.field }}{{ field.label_tag }} 15 {% else %} 16 {{ field.label_tag }}{{ field.field }} 17 {% endif %} 18 {% if field.field.field.help_text %}<p class="help">{{ field.field.field.help_text }}</p>{% endif %} 19 {% endfor %} 20 </div> 21 {% endfor %} 22 </fieldset> 23 {% endfor %} 24 </fieldset> 25 {% endfor %} 26 27 {% comment %} 28 <!-- Old forms. Here for reference until new forms match features --> 29 1 30 {% load admin_modify %} 2 31 <fieldset class="module aligned"> … … 15 44 {% endfor %} 16 45 </fieldset> 46 {% endcomment %} django/branches/newforms-admin/django/contrib/admin/templates/admin/edit_inline_tabular.html
r3571 r5473 1 {{ bound_inline.formset.management_form }} 2 <fieldset class="module"> 3 <h2>{{ bound_inline.verbose_name_plural|capfirst|escape }}</h2> 4 <table> 5 <thead><tr> 6 {% for field in bound_inline.fields %} 7 {% if not field.is_hidden %} 8 <th>{{ field.label|capfirst|escape }}</th> 9 {% endif %} 10 {% endfor %} 11 </tr></thead> 12 13 {% for bound_inline_object in bound_inline %} 14 15 <!-- still need optional original object --> 16 17 {% if bound_inline_object.form.form.errors %} 18 <tr class="errorlist"><td colspan="{{ bound_inline.fields|length }}"> 19 {{ bound_inline_object.form.form.errors }} 20 </tr> 21 {% endif %} 22 23 <tr class="{% cycle row1,row2 %}"> 24 {% for bfset in bound_inline_object.form %} 25 {% for line in bfset %} 26 {% for field in line %} 27 28 {% if not field.field.is_hidden %} 29 <td>{{ field.field }}</td> 30 {% else %} 31 {{ field.field }} 32 {% endif %} 33 {% endfor %} 34 {% endfor %} 35 {% endfor %} 36 37 <!-- still need optional view on site link --> 38 </tr> 39 40 {% endfor %} 41 42 </table> 43 44 <!-- still need for fcw in bound_related_object.form_field_collection_wrappers --> 45 46 </fieldset> 47 48 {% comment %} 49 <!-- Old forms. Here for reference until new forms match features --> 50 1 51 {% load admin_modify %} 2 52 <fieldset class="module"> … … 43 93 {% endfor %} 44 94 </fieldset> 95 {% endcomment %} django/branches/newforms-admin/django/contrib/admin/templatetags/admin_modify.py
r5195 r5473 105 105 return FieldWidgetNode(bits[1]) 106 106 field_widget = register.tag(field_widget) 107 108 class InlineNode(template.Node): 109 def __init__(self, inline_var): 110 self.inline_var = inline_var 111 112 def render(self, context): 113 inline = context[self.inline_var] 114 t = loader.get_template(inline.opts.template) 115 output = t.render(context) 116 return output 117 118 def render_inline(parser, token): 119 bits = token.contents.split() 120 if len(bits) != 2: 121 raise template.TemplateSyntaxError, "%s takes 1 argument" % bits[0] 122 return InlineNode(bits[1]) 123 render_inline = register.tag(render_inline) django/branches/newforms-admin/django/newforms/formsets.py
r5325 r5473 1 from django import newforms as forms 1 from forms import Form, ValidationError 2 from fields import IntegerField, BooleanField 3 from widgets import HiddenInput 2 4 3 5 # special field names … … 6 8 DELETION_FIELD_NAME = 'DELETE' 7 9 8 class ManagementForm( forms.Form):10 class ManagementForm(Form): 9 11 """ 10 12 ``ManagementForm`` is used to keep track of how many form instances … … 13 15 """ 14 16 def __init__(self, *args, **kwargs): 15 self.base_fields[FORM_COUNT_FIELD_NAME] = forms.IntegerField(widget=forms.HiddenInput)17 self.base_fields[FORM_COUNT_FIELD_NAME] = IntegerField(widget=HiddenInput) 16 18 super(ManagementForm, self).__init__(*args, **kwargs) 17 19 … … 34 36 else: 35 37 # not sure that ValidationError is the best thing to raise here 36 raise forms.ValidationError('ManagementForm data is missing or has been tampered with')38 raise ValidationError('ManagementForm data is missing or has been tampered with') 37 39 elif initial: 38 40 self.change_form_count = len(initial) … … 137 139 """A hook for adding extra fields on to each form instance.""" 138 140 if self.orderable: 139 form.fields[ORDERING_FIELD_NAME] = forms.IntegerField(label='Order', initial=index+1)141 form.fields[ORDERING_FIELD_NAME] = IntegerField(label='Order', initial=index+1) 140 142 if self.deletable: 141 form.fields[DELETION_FIELD_NAME] = forms.BooleanField(label='Delete', required=False)143 form.fields[DELETION_FIELD_NAME] = BooleanField(label='Delete', required=False) 142 144 143 145 def add_prefix(self, index): … … 152 154 attrs = {'form_class': form, 'num_extra': num_extra, 'orderable': orderable, 'deletable': deletable} 153 155 return type(form.__name__ + 'FormSet', (formset,), attrs) 156 157 def all_valid(formsets): 158 """Returns true if every formset in formsets is valid.""" 159 for formset in formsets: 160 if not formset.is_valid(): 161 return False 162 return True django/branches/newforms-admin/django/newforms/models.py
r5301 r5473 8 8 from util import ValidationError 9 9 from forms import BaseForm, SortedDictFromList 10 from fields import Field, ChoiceField 11 from widgets import Select, SelectMultiple, MultipleHiddenInput 10 from fields import Field, ChoiceField, IntegerField 11 from formsets import BaseFormSet, formset_for_form, DELETION_FIELD_NAME 12 from widgets import Select, SelectMultiple, HiddenInput, MultipleHiddenInput 12 13 13 14 __all__ = ( … … 198 199 final_values.append(obj) 199 200 return final_values 201 202 # Model-FormSet integration ################################################### 203 204 def initial_data(instance, fields=None): 205 """ 206 Return a dictionary from data in ``instance`` that is suitable for 207 use as a ``Form`` constructor's ``initial`` argument. 208 209 Provide ``fields`` to specify the names of specific fields to return. 210 All field values in the instance will be returned if ``fields`` is not 211 provided. 212 """ 213 model = instance.__class__ 214 opts = model._meta 215 initial = {} 216 for f in opts.fields + opts.many_to_many: 217 if not f.editable: 218 continue 219 if fields and not f.name in fields: 220 continue 221 initial[f.name] = f.value_from_object(instance) 222 return initial 223 224 class BaseModelFormSet(BaseFormSet): 225 """ 226 A ``FormSet`` attatched to a particular model or sequence of model instances. 227 """ 228 model = None 229 230 def __init__(self, data=None, auto_id='id_%s', prefix=None, instances=None): 231 self.instances = instances 232 kwargs = {'data': data, 'auto_id': auto_id, 'prefix': prefix} 233 if instances: 234 kwargs['initial'] = [initial_data(instance) for instance in instances] 235 super(BaseModelFormSet, self).__init__(**kwargs) 236 237 def save_new(self, form, commit=True): 238 """Saves and retrutns a new model instance for the given form.""" 239 return save_instance(form, self.model(), commit=commit) 240 241 def save_instance(self, form, instance, commit=True): 242 """Saves and retrutns an existing model instance for the given form.""" 243 return save_instance(form, instance, commit=commit) 244 245 def save(self, commit=True): 246 """Saves model instances for every form, adding and changing instances 247 as necessary, and returns the list of instances. 248 """ 249 saved_instances = [] 250 # put self.instances into a dict so they are easy to lookup by pk 251 instances = {} 252 for instance in self.instances: 253 instances[instance._get_pk_val()] = instance 254 if self.instances: 255 # update/save existing instances 256 for form in self.change_forms: 257 instance = instances[form.cleaned_data[self.model._meta.pk.attname]] 258 if form.cleaned_data[DELETION_FIELD_NAME]: 259 instance.delete() 260 else: 261 saved_instances.append(self.save_instance(form, instance, commit=commit)) 262 # create/save new instances 263 for form in self.add_forms: 264 if form.is_empty(): 265 continue 266 saved_instances.append(self.save_new(form, commit=commit)) 267 return saved_instances 268 269 def add_fields(self, form, index): 270 """Add a hidden field for the object's primary key.""" 271 form.fields[self.model._meta.pk.attname] = IntegerField(required=False, widget=HiddenInput) 272 super(BaseModelFormSet, self).add_fields(form, index) 273 274 def formset_for_model(model, form=BaseForm, formfield_callback=lambda f: f.formfield(), formset=BaseModelFormSet, extra=1, orderable=False, deletable=False, fields=None): 275 form = form_for_model(model, form=form, fields=fields, formfield_callback=formfield_callback) 276 FormSet = formset_for_form(form, formset, extra, orderable, deletable) 277 FormSet.model = model 278 return FormSet 279 280 class InlineFormset(BaseModelFormSet): 281 """A formset for child objects related to a parent.""" 282 def __init__(self, instance=None, data=None): 283 self.instance = instance 284 super(InlineFormset, self).__init__(data, instances=self.get_inline_objects()) 285 286 def get_inline_objects(self): 287 if self.instance is None: 288 return [] 289 from django.db.models.fields.related import RelatedObject 290 # is there a better way to get the object descriptor? 291 rel_name = RelatedObject(self.fk.rel.to, self.model, self.fk).get_accessor_name() 292 return getattr(self.instance, rel_name).all() 293 294 def save_new(self, form, commit=True): 295 kwargs = {self.fk.get_attname(): self.instance._get_pk_val()} 296 new_obj = self.model(**kwargs) 297 return save_instance(form, new_obj, commit=commit) 298 299 def inline_formset(parent_model, model, fk_name=None, fields=None, extra=3, formfield_callback=lambda f: f.formfield()): 300 """ 301 Returns an ``InlineFormset`` for the given kwargs. 302 303 You must provide ``fk_name`` if ``model`` has more than one ``ForeignKey`` 304 to ``parent_model``. 305 """ 306 from django.db.models import ForeignKey 307 opts = model._meta 308 # figure out what the ForeignKey from model to parent_model is 309 if fk_name is None: 310 fks_to_parent = [f for f in opts.fields if isinstance(f, ForeignKey) and f.rel.to == parent_model] 311 if len(fks_to_parent) == 1: 312 fk = fks_to_parent[0] 313 elif len(fks_to_parent) == 0: 314 raise Exception("%s has no ForeignKey to %s" % (model, parent_model)) 315 else: 316 raise Exception("%s has more than 1 ForeignKey to %s" % (model, parent_model)) 317 # let the formset handle object deletion by default 318 FormSet = formset_for_model(model, formset=InlineFormset, fields=fields, formfield_callback=formfield_callback, extra=extra, deletable=True) 319 # HACK: remove the ForeignKey to the parent from every form 320 # This should be done a line above before we pass 'fields' to formset_for_model 321 # an 'omit' argument would be very handy here 322 try: 323 del FormSet.form_class.base_fields[fk.name] 324 except KeyError: 325 pass 326 FormSet.parent_model = parent_model 327 FormSet.fk_name = fk.name 328 FormSet.fk = fk 329 return FormSet
