Table inheritance and polymorphic admin forms

464 views
Skip to first unread message

George Sakkis

unread,
Dec 21, 2008, 9:50:11 PM12/21/08
to Django users
Hi all,

I have a situation which I think would best be modeled as (single)
inheritance, involving product reviews for various kinds of products.
Naturally, there are some fields which are common to all reviews
(overall rating, user, review date) and some which are specific to the
type of product. Given such an appropriate model set, I'd like to use
the admin interface to, say, edit the reviews of a user "inline",
where for each review the appropriate form would be rendered (i.e.
including all the fields related to each given product). Is this
possible, and if not, is there a workaround ?

George

Malcolm Tredinnick

unread,
Dec 21, 2008, 10:02:15 PM12/21/08
to django...@googlegroups.com

My inner philosopher is protesting that if there is a workaround, that
would make it possible to do something, right? So if it's not possible,
there's no workaround, pretty much by definition. :-)

Anyway, there are two answers to your question: (1) yes, it Just Works,
and (2) What happened when you tried it? It's not really that hard to
test this out.

Create a simple model inheritance situation with Parent and Child,
inheriting from Parent. Give the Child and admin entry and go and add a
new child. You'll see all the Parent fields right there on the page. It
works transparently.

Regards,
Malcolm

George Sakkis

unread,
Dec 22, 2008, 12:42:24 PM12/22/08
to Django users
On Dec 21, 10:02 pm, Malcolm Tredinnick <malc...@pointy-stick.com>
wrote:

> On Sun, 2008-12-21 at 18:50 -0800, George Sakkis wrote:
> > Hi all,
>
> > I have a situation which I think would best be modeled as (single)
> > inheritance, involving product reviews for various kinds of products.
> > Naturally, there are some fields which are common to all reviews
> > (overall rating, user, review date) and some which are specific to the
> > type of product. Given such an appropriate model set, I'd like to use
> > the admin interface to, say, edit the reviews of a user "inline",
> > where for each review the appropriate form would be rendered (i.e.
> > including all the fields related to each given product). Is this
> > possible, and if not, is there a workaround ?
>
> My inner philosopher is protesting that if there is a workaround, that
> would make it possible to do something, right? So if it's not possible,
> there's no workaround, pretty much by definition. :-)

Well a more precise wording would be "is there a straightforward,
'correct', solution, or if not, at least an ugly kludge that at least
works ?" :-)

> Anyway, there are two answers to your question: (1) yes, it Just Works,
> and (2) What happened when you tried it? It's not really that hard to
> test this out.
>
> Create a simple model inheritance situation with Parent and Child,
> inheriting from Parent. Give the Child and admin entry and go and add a
> new child. You'll see all the Parent fields right there on the page. It
> works transparently.

Unfortunately it doesn't work for what I tried. What I am trying to do
is have the Child classes as InlineModelAdmin in some other class that
references them. Here's a sample:

# ======= models.py ===================
class Review(models.Model):
# general fields
class Meta:
abstract = True

class BookReview(Review):
# book-specific review fields

class MoviewReview(Review):
# movie-specific review fields

# ======= admin.py ===================

class ReviewInline(admin.StackedInline):
model = models.Review
extra = 0

class UserReviewAdmin(admin.ModelAdmin):
inlines = [ReviewInline]
# more stuff

admin.site.register(models.User, UserReviewAdmin)


This dies with "type object 'Review' has no attribute
'_default_manager'", apparently because Review is not a table-backed
model. What should I be doing differently ?

George

Malcolm Tredinnick

unread,
Dec 22, 2008, 7:04:12 PM12/22/08
to django...@googlegroups.com
On Mon, 2008-12-22 at 09:42 -0800, George Sakkis wrote:
[...]

> Unfortunately it doesn't work for what I tried. What I am trying to do
> is have the Child classes as InlineModelAdmin in some other class that
> references them. Here's a sample:
>
> # ======= models.py ===================
> class Review(models.Model):
> # general fields
> class Meta:
> abstract = True
>
> class BookReview(Review):
> # book-specific review fields
>
> class MoviewReview(Review):
> # movie-specific review fields
>
> # ======= admin.py ===================
>
> class ReviewInline(admin.StackedInline):
> model = models.Review
> extra = 0
>
> class UserReviewAdmin(admin.ModelAdmin):
> inlines = [ReviewInline]
> # more stuff
>
> admin.site.register(models.User, UserReviewAdmin)
>
>
> This dies with "type object 'Review' has no attribute
> '_default_manager'", apparently because Review is not a table-backed
> model. What should I be doing differently ?

I don't really understand at all why you're trying to create the admin
like this. You want to edit BookReview and MovieReview objects, so
create admin classes for those two models. Creating an admin class for
Review is impossible (since it's abstract -- which wasn't something I
realised you were doing in your original explanation) and not really
necessary, since it's not Review objects that you're editing.

Regards,
Malcolm


George Sakkis

unread,
Dec 23, 2008, 1:17:48 AM12/23/08
to Django users
On Dec 22, 7:04 pm, Malcolm Tredinnick <malc...@pointy-stick.com>
wrote:
Thanks, that's what I eventually did, and it sort of works. There are
at least two reasons though I'd like to be able to declare generic
Reviews:

1. With explicit concrete inline admin classes, the interface shows
one section per class ("Book reviews", "Movie reviews", etc.). This
might be a feature under some circumstances (you get a "group by
review type" for free), but an inconvenience if you want to show a
single list of all reviews regardless of type (a more real use case is
entities that have children of a single review class only, e.g. only
movie reviews; these will still show up sections for all review types,
although it is guaranteed that only one section will be non-empty).

2. The main problem though is that it essentially cancels the major
benefit of inheritance: polymorphism. Every time I add a new Review
model subclass, I have to add a respective ModelAdmin subclass *and*
add it explicitly to the inlines of every ModelAdmin that handles
lists of reviews, i.e. change at least three places instead of one.
Apparently inheritance doesn't buy me much this way; might as well
code each review type completely independently. It would be neat if by
declaring inlines = [ReviewInline], there was a single "Reviews"
section listing all child reviews, regardless of type. A future
feature request maybe ?

George

Malcolm Tredinnick

unread,
Dec 23, 2008, 1:34:03 AM12/23/08
to django...@googlegroups.com
On Mon, 2008-12-22 at 22:17 -0800, George Sakkis wrote:
[...]

> 1. With explicit concrete inline admin classes, the interface shows


> one section per class ("Book reviews", "Movie reviews", etc.). This
> might be a feature under some circumstances (you get a "group by
> review type" for free), but an inconvenience if you want to show a
> single list of all reviews regardless of type (a more real use case is
> entities that have children of a single review class only, e.g. only
> movie reviews; these will still show up sections for all review types,
> although it is guaranteed that only one section will be non-empty).

You're talking about the *admin* interface here. It's really primarily
for data entry. Stuff like "showing a list of combined reviews" isn't
something you're going to use the admin interface to do.

>
> 2. The main problem though is that it essentially cancels the major
> benefit of inheritance: polymorphism.

That's subjective. Inheritance also provides a way to factor out
commonality and *that* is the benefit that is being exploited at the
boundary between object-based and relation-based data structures that
we're working with here. Polymorphism is not a concept that translates
well into the relational database model and is intentionally left as
being a bit of a manual process for that reason.

> Every time I add a new Review
> model subclass, I have to add a respective ModelAdmin subclass *and*
> add it explicitly to the inlines of every ModelAdmin that handles
> lists of reviews, i.e. change at least three places instead of one.
> Apparently inheritance doesn't buy me much this way; might as well
> code each review type completely independently. It would be neat if by
> declaring inlines = [ReviewInline], there was a single "Reviews"
> section listing all child reviews, regardless of type.

Um .. it sounds to me like if you were doing this a lot you would be
writing a subclass that could handle a lot of this automatically. Just
because it doesn't work out of the box doesn't mean you shouldn't write
code to do it.

Ultimately, it seems that you have some particular data entry
requirements that aren't met by the admin out of the box. Fortunately,
the admin is heavily customisable by writing Python code. So have at it.

Malcolm

Reply all
Reply to author
Forward
0 new messages