Permalink
Browse files

Fixed #10271, #10281 -- Fixed the handling multiple inline models tha…

…t share a common base class and have the link to the inline parent on the base class. Includes modifications that allow the equivalent handling for GenericFields. Thanks to Idan Gazit, Antti Kaihola (akaihola), and Alex Gaynor for their work on this patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10017 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent d0fff8c commit 3c8568a7dcb38aaa5aceec4f20c3ec3f0217a50d @freakboy3742 freakboy3742 committed Mar 10, 2009
View
@@ -161,6 +161,7 @@ answer newbie questions, and generally made Django that much better:
Marc Garcia <marc.garcia@accopensys.com>
Alex Gaynor <alex.gaynor@gmail.com>
Andy Gayton <andy-django@thecablelounge.com>
+ Idan Gazit
Baishampayan Ghose
Dimitris Glezos <dimitris@glezos.com>
glin@seznam.cz
@@ -522,10 +522,16 @@ def add_view(self, request, form_url='', extra_context=None):
else:
form_validated = False
new_object = self.model()
+ prefixes = {}
for FormSet in self.get_formsets(request):
+ prefix = FormSet.get_default_prefix()
+ prefixes[prefix] = prefixes.get(prefix, 0) + 1
+ if prefixes[prefix] != 1:
+ prefix = "%s-%s" % (prefix, prefixes[prefix])
formset = FormSet(data=request.POST, files=request.FILES,
instance=new_object,
- save_as_new=request.POST.has_key("_saveasnew"))
+ save_as_new=request.POST.has_key("_saveasnew"),
+ prefix=prefix)
formsets.append(formset)
if all_valid(formsets) and form_validated:
self.save_model(request, new_object, form, change=False)
@@ -547,8 +553,13 @@ def add_view(self, request, form_url='', extra_context=None):
if isinstance(f, models.ManyToManyField):
initial[k] = initial[k].split(",")
form = ModelForm(initial=initial)
+ prefixes = {}
for FormSet in self.get_formsets(request):
- formset = FormSet(instance=self.model())
+ prefix = FormSet.get_default_prefix()
+ prefixes[prefix] = prefixes.get(prefix, 0) + 1
+ if prefixes[prefix] != 1:
+ prefix = "%s-%s" % (prefix, prefixes[prefix])
+ formset = FormSet(instance=self.model(), prefix=prefix)
formsets.append(formset)
adminForm = helpers.AdminForm(form, list(self.get_fieldsets(request)), self.prepopulated_fields)
@@ -608,9 +619,14 @@ def change_view(self, request, object_id, extra_context=None):
else:
form_validated = False
new_object = obj
+ prefixes = {}
for FormSet in self.get_formsets(request, new_object):
+ prefix = FormSet.get_default_prefix()
+ prefixes[prefix] = prefixes.get(prefix, 0) + 1
+ if prefixes[prefix] != 1:
+ prefix = "%s-%s" % (prefix, prefixes[prefix])
formset = FormSet(request.POST, request.FILES,
- instance=new_object)
+ instance=new_object, prefix=prefix)
formsets.append(formset)
if all_valid(formsets) and form_validated:
@@ -625,8 +641,13 @@ def change_view(self, request, object_id, extra_context=None):
else:
form = ModelForm(instance=obj)
+ prefixes = {}
for FormSet in self.get_formsets(request, obj):
- formset = FormSet(instance=obj)
+ prefix = FormSet.get_default_prefix()
+ prefixes[prefix] = prefixes.get(prefix, 0) + 1
+ if prefixes[prefix] != 1:
+ prefix = "%s-%s" % (prefix, prefixes[prefix])
+ formset = FormSet(instance=obj, prefix=prefix)
formsets.append(formset)
adminForm = helpers.AdminForm(form, self.get_fieldsets(request, obj), self.prepopulated_fields)
@@ -291,7 +291,7 @@ class BaseGenericInlineFormSet(BaseModelFormSet):
ct_field_name = "content_type"
ct_fk_field_name = "object_id"
- def __init__(self, data=None, files=None, instance=None, save_as_new=None):
+ def __init__(self, data=None, files=None, instance=None, save_as_new=None, prefix=None):
opts = self.model._meta
self.instance = instance
self.rel_name = '-'.join((
@@ -300,9 +300,17 @@ def __init__(self, data=None, files=None, instance=None, save_as_new=None):
))
super(BaseGenericInlineFormSet, self).__init__(
queryset=self.get_queryset(), data=data, files=files,
- prefix=self.rel_name
+ prefix=prefix
)
+ #@classmethod
+ def get_default_prefix(cls):
+ opts = cls.model._meta
+ return '-'.join((opts.app_label, opts.object_name.lower(),
+ cls.ct_field.name, cls.ct_fk_field.name,
+ ))
+ get_default_prefix = classmethod(get_default_prefix)
+
def get_queryset(self):
# Avoid a circular import.
from django.contrib.contenttypes.models import ContentType
@@ -32,7 +32,7 @@ class BaseFormSet(StrAndUnicode):
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
initial=None, error_class=ErrorList):
self.is_bound = data is not None or files is not None
- self.prefix = prefix or 'form'
+ self.prefix = prefix or self.get_default_prefix()
self.auto_id = auto_id
self.data = data
self.files = files
@@ -62,7 +62,7 @@ def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
initial = {TOTAL_FORM_COUNT: self._total_form_count,
INITIAL_FORM_COUNT: self._initial_form_count}
self.management_form = ManagementForm(initial=initial, auto_id=self.auto_id, prefix=self.prefix)
-
+
# construct the forms in the formset
self._construct_forms()
@@ -74,7 +74,7 @@ def _construct_forms(self):
self.forms = []
for i in xrange(self._total_form_count):
self.forms.append(self._construct_form(i))
-
+
def _construct_form(self, i, **kwargs):
"""
Instantiates and returns the i-th form instance in a formset.
@@ -118,7 +118,7 @@ def _get_cleaned_data(self):
def _get_deleted_forms(self):
"""
- Returns a list of forms that have been marked for deletion. Raises an
+ Returns a list of forms that have been marked for deletion. Raises an
AttributeError if deletion is not allowed.
"""
if not self.is_valid() or not self.can_delete:
@@ -176,6 +176,11 @@ def compare_ordering_values(x, y):
return [self.forms[i[0]] for i in self._ordering]
ordered_forms = property(_get_ordered_forms)
+ #@classmethod
+ def get_default_prefix(cls):
+ return 'form'
+ get_default_prefix = classmethod(get_default_prefix)
+
def non_form_errors(self):
"""
Returns an ErrorList of errors that aren't associated with a particular
@@ -249,8 +249,8 @@ def validate_unique(self):
# This is an extra field that's not on the ModelForm, ignore it
continue
if not isinstance(f, ModelField):
- # This is an extra field that happens to have a name that matches,
- # for example, a related object accessor for this model. So
+ # This is an extra field that happens to have a name that matches,
+ # for example, a related object accessor for this model. So
# get_field_by_name found it, but it is not a Field so do not proceed
# to use it as if it were.
continue
@@ -472,7 +472,7 @@ def __init__(self, data=None, files=None, instance=None,
# is there a better way to get the object descriptor?
self.rel_name = RelatedObject(self.fk.rel.to, self.model, self.fk).get_accessor_name()
qs = self.model._default_manager.filter(**{self.fk.name: self.instance})
- super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix or self.rel_name,
+ super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix,
queryset=qs)
def _construct_forms(self):
@@ -489,6 +489,12 @@ def _construct_form(self, i, **kwargs):
form.data[form.add_prefix(self._pk_field.name)] = None
return form
+ #@classmethod
+ def get_default_prefix(cls):
+ from django.db.models.fields.related import RelatedObject
+ return RelatedObject(cls.fk.rel.to, cls.model, cls.fk).get_accessor_name()
+ get_default_prefix = classmethod(get_default_prefix)
+
def save_new(self, form, commit=True):
fk_attname = self.fk.get_attname()
kwargs = {fk_attname: self.instance.pk}
@@ -238,4 +238,9 @@ def __unicode__(self):
<p><label for="id_generic_relations-taggeditem-content_type-object_id-1-tag">Tag:</label> <input id="id_generic_relations-taggeditem-content_type-object_id-1-tag" type="text" name="generic_relations-taggeditem-content_type-object_id-1-tag" maxlength="50" /></p>
<p><label for="id_generic_relations-taggeditem-content_type-object_id-1-DELETE">Delete:</label> <input type="checkbox" name="generic_relations-taggeditem-content_type-object_id-1-DELETE" id="id_generic_relations-taggeditem-content_type-object_id-1-DELETE" /><input type="hidden" name="generic_relations-taggeditem-content_type-object_id-1-id" id="id_generic_relations-taggeditem-content_type-object_id-1-id" /></p>
+>>> formset = GenericFormSet(instance=lion, prefix='x')
+>>> for form in formset.forms:
+... print form.as_p()
+<p><label for="id_x-0-tag">Tag:</label> <input id="id_x-0-tag" type="text" name="x-0-tag" maxlength="50" /></p>
+<p><label for="id_x-0-DELETE">Delete:</label> <input type="checkbox" name="x-0-DELETE" id="id_x-0-DELETE" /><input type="hidden" name="x-0-id" id="id_x-0-id" /></p>
"""}
@@ -20,7 +20,7 @@ class Article(models.Model):
def __unicode__(self):
return self.title
-
+
def model_year(self):
return self.date.year
model_year.admin_order_field = 'date'
@@ -54,14 +54,14 @@ class Meta:
class ChapterXtra1(models.Model):
chap = models.OneToOneField(Chapter, verbose_name=u'¿Chap?')
- xtra = models.CharField(max_length=100, verbose_name=u'¿Xtra?')
+ xtra = models.CharField(max_length=100, verbose_name=u'¿Xtra?')
def __unicode__(self):
return u'¿Xtra1: %s' % self.xtra
class ChapterXtra2(models.Model):
chap = models.OneToOneField(Chapter, verbose_name=u'¿Chap?')
- xtra = models.CharField(max_length=100, verbose_name=u'¿Xtra?')
+ xtra = models.CharField(max_length=100, verbose_name=u'¿Xtra?')
def __unicode__(self):
return u'¿Xtra2: %s' % self.xtra
@@ -87,7 +87,7 @@ def changelist_view(self, request):
'extra_var': 'Hello!'
}
)
-
+
def modeladmin_year(self, obj):
return obj.date.year
modeladmin_year.admin_order_field = 'date'
@@ -121,7 +121,7 @@ def __unicode__(self):
class Color(models.Model):
value = models.CharField(max_length=10)
- warm = models.BooleanField()
+ warm = models.BooleanField()
def __unicode__(self):
return self.value
@@ -134,12 +134,56 @@ def __unicode__(self):
class ThingAdmin(admin.ModelAdmin):
list_filter = ('color',)
+class Persona(models.Model):
+ """
+ A simple persona associated with accounts, to test inlining of related
+ accounts which inherit from a common accounts class.
+ """
+ name = models.CharField(blank=False, max_length=80)
+ def __unicode__(self):
+ return self.name
+
+class Account(models.Model):
+ """
+ A simple, generic account encapsulating the information shared by all
+ types of accounts.
+ """
+ username = models.CharField(blank=False, max_length=80)
+ persona = models.ForeignKey(Persona, related_name="accounts")
+ servicename = u'generic service'
+
+ def __unicode__(self):
+ return "%s: %s" % (self.servicename, self.username)
+
+class FooAccount(Account):
+ """A service-specific account of type Foo."""
+ servicename = u'foo'
+
+class BarAccount(Account):
+ """A service-specific account of type Bar."""
+ servicename = u'bar'
+
+class FooAccountAdmin(admin.StackedInline):
+ model = FooAccount
+ extra = 1
+
+class BarAccountAdmin(admin.StackedInline):
+ model = BarAccount
+ extra = 1
+
+class PersonaAdmin(admin.ModelAdmin):
+ inlines = (
+ FooAccountAdmin,
+ BarAccountAdmin
+ )
+
admin.site.register(Article, ArticleAdmin)
admin.site.register(CustomArticle, CustomArticleAdmin)
admin.site.register(Section, inlines=[ArticleInline])
admin.site.register(ModelWithStringPrimaryKey)
admin.site.register(Color)
admin.site.register(Thing, ThingAdmin)
+admin.site.register(Persona, PersonaAdmin)
# We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2.
# That way we cover all four cases:
@@ -153,3 +197,5 @@ class ThingAdmin(admin.ModelAdmin):
admin.site.register(Book, inlines=[ChapterInline])
admin.site.register(Promo)
admin.site.register(ChapterXtra1)
+
+
Oops, something went wrong. Retry.

0 comments on commit 3c8568a

Please sign in to comment.