Django

Code

Ticket #4667: newforms-admin-6477-generic-edit-inline.patch

File newforms-admin-6477-generic-edit-inline.patch, 9.8 kB (added by Honza_Kral, 1 year ago)

Matching django version 6477

  • a/django/contrib/admin/options.py

    old new  
    11from django import oldforms, template 
    22from django import newforms as forms 
    33from django.newforms.formsets import all_valid 
    4 from django.contrib.contenttypes.models import ContentType 
    54from django.contrib.admin import widgets 
    65from django.contrib.admin.util import get_deleted_objects 
    76from django.core.exceptions import ImproperlyConfigured, PermissionDenied 
     
    451450            return HttpResponseRedirect("../") 
    452451 
    453452    def render_change_form(self, model, context, add=False, change=False, form_url=''): 
     453        from django.contrib.contenttypes.models import ContentType 
    454454        opts = model._meta 
    455455        app_label = opts.app_label 
    456456        ordered_objects = opts.get_ordered_objects() 
     
    721721    template = None 
    722722    verbose_name = None 
    723723    verbose_name_plural = None 
     724    formset = None 
    724725 
    725726    def __init__(self, parent_model, admin_site): 
    726727        self.admin_site = admin_site 
     
    738739            fields = flatten_fieldsets(self.declared_fieldsets) 
    739740        else: 
    740741            fields = None 
    741         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) 
     742        return forms.inline_formset(self.parent_model, self.model, fk_name=self.fk_name, fields=fields, 
     743                    formfield_callback=self.formfield_for_dbfield, extra=self.extra, formset=self.formset) 
    742744 
    743745    def formset_change(self, request, obj): 
    744746        """Returns an InlineFormSet class for use in admin change views.""" 
     
    746748            fields = flatten_fieldsets(self.declared_fieldsets) 
    747749        else: 
    748750            fields = None 
    749         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) 
     751        return forms.inline_formset(self.parent_model, self.model, fk_name=self.fk_name, fields=fields, 
     752                    formfield_callback=self.formfield_for_dbfield, extra=self.extra, formset=self.formset) 
    750753 
    751754    def fieldsets_add(self, request): 
    752755        if self.declared_fieldsets: 
  • a/django/contrib/contenttypes/generic.py

    old new  
    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, instances=self.get_inline_objects(), prefix=self.rel_name) 
     316 
     317    def get_inline_objects(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 
  • a/django/db/models/query.py

    old new  
    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 
     
    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() 
  • a/django/newforms/models.py

    old new  
    338338            raise Exception("%s has more than 1 ForeignKey to %s" % (model, parent_model)) 
    339339    return fk 
    340340 
    341 def inline_formset(parent_model, model, fk_name=None, fields=None, extra=3, orderable=False, deletable=True, formfield_callback=lambda f: f.formfield()): 
     341def inline_formset(parent_model, model, fk_name=None, fields=None, extra=3, orderable=False, deletable=True, formfield_callback=lambda f: f.formfield(), formset=None): 
    342342    """ 
    343343    Returns an ``InlineFormset`` for the given kwargs. 
    344344 
     
    347347    """ 
    348348    fk = get_foreign_key(parent_model, model, fk_name=fk_name) 
    349349    # let the formset handle object deletion by default 
    350     FormSet = formset_for_model(model, formset=InlineFormset, fields=fields, 
     350    FormSet = formset_for_model(model, formset=formset or InlineFormset, fields=fields, 
    351351                                formfield_callback=formfield_callback, 
    352352                                extra=extra, orderable=orderable, 
    353353                                deletable=deletable)