Ticket #4667: 4667-generic_edit_inline.diff

File 4667-generic_edit_inline.diff, 6.0 KB (added by Honza Král, 17 years ago)

new two-part patch for django @6657 (second part)

  • django/contrib/contenttypes/generic.py

    commit 8ecda951e8ded8fadf9e54dbeb1b7c444d88e3d2
    Author: Honza Král <Honza.Kral@gmail.com>
    Date:   Sun Nov 11 21:32:22 2007 +0100
    
        Support for edit_inline for generic relations
        
        http://code.djangoproject.com/ticket/4667
    
    diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py
    index b738a26..ba45d4d 100644
    a b class GenericRel(ManyToManyRel):  
    260260        self.multiple = True
    261261        assert not (self.raw_id_admin and self.filter_interface), \
    262262            "Generic relations may not use both raw_id_admin and filter_interface"
     263
     264from django.newforms.models import BaseModelFormSet, formset_for_model, save_instance
     265from django.contrib.admin.options import InlineModelAdmin, flatten_fieldsets
     266
     267def get_foreign_key(model, ct_field_name, id_field_name):
     268    opts = model._meta
     269    # avoid circular import
     270    from django.db.models import ForeignKey
     271    from django.contrib.contenttypes.models import ContentType
     272    opts = model._meta
     273
     274    # if there is no field called `ct_field_name` let the exception propagate
     275    ct = opts.get_field(ct_field_name)
     276    if not isinstance(ct, ForeignKey) or ct.rel.to != ContentType:
     277        raise Exception("fk_name '%s' is not a ForeignKey to ContentType" % (ct_field_name))
     278
     279    # if there is no field called `id_field_name` let the exception propagate
     280    obj_id = opts.get_field(id_field_name)
     281
     282    return ct, obj_id
     283
     284def inline_formset(model, ct_field_name, id_field_name, fields=None, extra=3, orderable=False, deletable=True, formfield_callback=lambda f: f.formfield(), formset=None):
     285    """
     286    Returns an ``InlineFormset`` for the given kwargs.
     287
     288    You must provide ``fk_name`` if ``model`` has more than one ``ForeignKey``
     289    to ``parent_model``.
     290    """
     291    ct, obj_id = get_foreign_key(model, ct_field_name, id_field_name)
     292    # let the formset handle object deletion by default
     293    FormSet = formset_for_model(model, formset=formset or GenericInlineFormset, fields=fields,
     294                                formfield_callback=formfield_callback,
     295                                extra=extra, orderable=orderable,
     296                                deletable=deletable)
     297    # HACK: remove the ForeignKey to the parent from every form
     298    # This should be done a line above before we pass 'fields' to formset_for_model
     299    # an 'omit' argument would be very handy here
     300    try:
     301        del FormSet.form_class.base_fields[ct.name]
     302        del FormSet.form_class.base_fields[obj_id.name]
     303    except KeyError:
     304        pass
     305    FormSet.ct = ct
     306    FormSet.obj_id = obj_id
     307    return FormSet
     308
     309class GenericInlineFormset(BaseModelFormSet):
     310    """A formset for child objects related to a parent."""
     311    def __init__(self, instance=None, data=None, files=None):
     312        self.instance = instance
     313        # is there a better way to get the object descriptor?
     314        self.rel_name = self.ct.name + '_' + self.obj_id.name
     315        super(GenericInlineFormset, self).__init__(data, files, prefix=self.rel_name)
     316
     317    def get_queryset(self):
     318        # This import is done here to avoid circular import importing this module
     319        from django.contrib.contenttypes.models import ContentType
     320        if self.instance is None:
     321            return []
     322        return self.model._default_manager.filter( **{
     323                self.ct.name : ContentType.objects.get_for_model(self.instance),
     324                self.obj_id.name : self.instance._get_pk_val()
     325            })
     326
     327    def save_new(self, form, commit=True):
     328        # This import is done here to avoid circular import importing this module
     329        from django.contrib.contenttypes.models import ContentType
     330        kwargs = {
     331            self.ct.get_attname(): ContentType.objects.get_for_model(self.instance).id,
     332            self.obj_id.get_attname(): self.instance._get_pk_val(),
     333        }
     334        new_obj = self.model(**kwargs)
     335        return save_instance(form, new_obj, commit=commit)
     336
     337class GenericInlineModelAdmin(InlineModelAdmin):
     338    ct_field_name = None
     339    id_field_name = None
     340    def formset_add(self, request):
     341        """Returns an GenericInlineFormSet class for use in admin add views."""
     342        if self.declared_fieldsets:
     343            fields = flatten_fieldsets(self.declared_fieldsets)
     344        else:
     345            fields = None
     346        return inline_formset(self.model,  self.ct_field_name, self.id_field_name, fields=fields,
     347                    formfield_callback=self.formfield_for_dbfield, extra=self.extra, formset=self.formset)
     348
     349    def formset_change(self, request, obj):
     350        """Returns an GenericInlineFormSet class for use in admin change views."""
     351        if self.declared_fieldsets:
     352            fields = flatten_fieldsets(self.declared_fieldsets)
     353        else:
     354            fields = None
     355        return inline_formset(self.model,  self.ct_field_name, self.id_field_name, fields=fields,
     356                    formfield_callback=self.formfield_for_dbfield, extra=self.extra, formset=self.formset)
     357
     358class GenericStackedInline(GenericInlineModelAdmin):
     359    template = 'admin/edit_inline/stacked.html'
     360
     361class GenericTabularInline(GenericInlineModelAdmin):
     362    template = 'admin/edit_inline/tabular.html'
     363
  • django/db/models/query.py

    diff --git a/django/db/models/query.py b/django/db/models/query.py
    index 4d0d295..636ae72 100644
    a b from django.db.models import signals, loading  
    55from django.dispatch import dispatcher
    66from django.utils.datastructures import SortedDict
    77from django.utils.encoding import smart_unicode
    8 from django.contrib.contenttypes import generic
    98import datetime
    109import operator
    1110import re
    def lookup_inner(path, lookup_type, value, opts, table, column):  
    11191118
    11201119def delete_objects(seen_objs):
    11211120    "Iterate through a list of seen classes, and remove any instances that are referred to"
     1121    from django.contrib.contenttypes import generic
    11221122    qn = connection.ops.quote_name
    11231123    ordered_classes = seen_objs.keys()
    11241124    ordered_classes.reverse()
Back to Top