Why doesn't my isinstance() work?

1,567 views
Skip to first unread message

Lachlan Musicman

unread,
Apr 21, 2010, 2:08:24 AM4/21/10
to django...@googlegroups.com
Hola,

I'm having trouble getting a subclass to trigger an isinstance(). Code below:

model.py excerpt:
------------
from django.db import models
from django.forms import ModelForm

class Author(models.Model):
first = models.CharField(u'First Name', max_length=20)
other = models.CharField(u'Other Names', max_length=20, blank=True)
last = models.CharField(u'Last Name', max_length=20)

class Meta:
ordering = ['last']

def __unicode__(self):
return u'%s %s' % (self.first, self.last)

def get_absolute_url(self):
return "/author/%i" % self.str()

class Translator(Author):
language = models.CharField(max_length=10, choices=LANGUAGES)

class Meta:
verbose_name = 'Translator'
verbose_name_plural = 'Translators'
------------

views.py excerpt:
------------
from django.shortcuts import render_to_response, get_object_or_404
from booktrans.books.models import *
from django.template import RequestContext
from django.http import HttpResponseRedirect

def index(request):
all_authors = Author.objects.all()
all_origAuthors = []
all_translators = []
for author in all_authors:
if isinstance(author, Translator):
all_translators.append(author)
else:
all_origAuthors.append(author)
return render_to_response('books/index.html',locals())

------------

And from the shell:

>>> from booktrans.books.models import *
>>> t = Translator.objects.all()
>>> t
[<Translator: qwerqwad asdasd>, <Translator: Yoko Miyagi>]
>>> t = Translator.objects.get(pk=2)
>>> t
<Translator: Yoko Miyagi>
>>> isinstance(t, Translator)
True
>>> a = Author.objects.get(pk=1)
>>> isinstance(a, Translator)
False



Any tips?

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django...@googlegroups.com.
To unsubscribe from this group, send email to django-users...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.

Daniel Roseman

unread,
Apr 21, 2010, 5:33:09 AM4/21/10
to Django users
On Apr 21, 7:08 am, Lachlan Musicman <data...@gmail.com> wrote:
> Hola,
>
> I'm having trouble getting a subclass to trigger an isinstance(). Code below:
>
> model.py excerpt:
> ------------
> from django.db import models
> from django.forms import ModelForm
>
> class Author(models.Model):
>   first = models.CharField(u'First Name', max_length=20)
>   other = models.CharField(u'Other Names', max_length=20, blank=True)
>   last = models.CharField(u'Last Name', max_length=20)
>
>   class Meta:
>     ordering = ['last']
>
>   def __unicode__(self):
>     return u'%s %s' % (self.first, self.last)
>
>   def get_absolute_url(self):
>     return "/author/%i" % self.str()
>
> class Translator(Author):
>   language = models.CharField(max_length=10, choices=LANGUAGES)
>
>   class Meta:
>     verbose_name = 'Translator'
>     verbose_name_plural = 'Translators'

<snip>

> And from the shell:
>
> >>> from booktrans.books.models import *
> >>> t = Translator.objects.all()
> >>> t
>
> [<Translator: qwerqwad asdasd>, <Translator: Yoko Miyagi>]>>> t = Translator.objects.get(pk=2)
> >>> t
>
> <Translator: Yoko Miyagi>>>> isinstance(t, Translator)
> True
> >>> a = Author.objects.get(pk=1)
> >>> isinstance(a, Translator)
>
> False
>
> Any tips?

This is the expected behaviour. You've asked for an Author object, so
that's what you've got. There's no way to tell from the Author model
that this particular instance of it is also an instance of Translator.
--
DR.

Torsten Bronger

unread,
Apr 21, 2010, 7:18:55 AM4/21/10
to django...@googlegroups.com
Hallöchen!

Daniel Roseman writes:

> [...]
>
> This is the expected behaviour. You've asked for an Author object,
> so that's what you've got. There's no way to tell from the Author
> model that this particular instance of it is also an instance of
> Translator.

More and more I get the impression that this is a significant use
case. Again, here is how we solved it: Append the following source
code to your models.py and call instance.find_actual_instance(),
then you get the, well, actual instance. Maybe something like this
should become part of Django? The clean solution is to use
contenttypes instead of the "try all subclasses" trick, though. I
just haven't had time to do it.


_globals = copy.copy(globals())
all_models = [cls for cls in _globals.values() if inspect.isclass(cls) and issubclass(cls, models.Model)]
class_hierarchy = inspect.getclasstree(all_models)
def find_actual_instance(self):
u"""This is a module function but is is injected into ``Models.model`` to
become a class method for all models of Chantal. If you call this method
on a database instance, you get the leaf class instance of this
model. This way, polymorphism actually works with the
relational database.

:Return:
an instance of the actual model class for this database entry.

:rtype: ``models.Model``.
"""
try:
return self.__actual_instance
except AttributeError:
if not self.direct_subclasses:
self.__actual_instance = self
else:
for cls in self.direct_subclasses:
name = cls.__name__.lower()
if hasattr(self, name):
instance = getattr(self, name)
self.__actual_instance = instance.find_actual_instance()
break
else:
raise Exception("internal error: instance not found")
return self.__actual_instance

models.Model.find_actual_instance = find_actual_instance


def inject_direct_subclasses(parent, hierarchy):
u"""This is a mere helper function which injects a list with all subclasses
into the class itself, under the name ``direct_subclasses``. It is only
for use by `find_actual_instance`.

This is basically a tree walker through the weird nested data structure
returned by ``inspect.getclasstree`` and stored in `class_hierarchy`.

:Parameters:
- `parent`: the class to which the subclasses should be added
- `hierarchy`: the remaining class inheritance hierarchy that has to be
processed.

:type parent: class, descendant of ``models.Model``
:type hierarchy: list as returned by ``inspect.getclasstree``
"""
i = 0
while i < len(hierarchy):
# May have already been initialised by another app
if "direct_subclasses" not in hierarchy[i][0].__dict__:
hierarchy[i][0].direct_subclasses = set()
if parent:
parent.direct_subclasses.add(hierarchy[i][0])
if i + 1 < len(hierarchy) and isinstance(hierarchy[i+1], list):
inject_direct_subclasses(hierarchy[i][0], hierarchy[i+1])
i += 2
else:
i += 1

inject_direct_subclasses(None, class_hierarchy)
del _globals, cls



Tschö,
Torsten.

--
Torsten Bronger, aquisgrana, europa vetus
Jabber ID: torsten...@jabber.rwth-aachen.de
or http://bronger-jmp.appspot.com

Tim Shaffer

unread,
Apr 21, 2010, 8:04:27 AM4/21/10
to Django users
Why should an author instance ever return True for
isinsnace(author_instance, Translator). That's not really the way
Python or Django are designed to work.

Why not just check to see if the author instance has an associated
translator instance? That's the proper way. See the docs on multi-
table inheritance.

http://docs.djangoproject.com/en/dev/topics/db/models/#id7

>>> a = Author.objects.get(pk=1)
>>> isinstance(a.translator, Translator)
True

Torsten Bronger

unread,
Apr 21, 2010, 8:25:47 AM4/21/10
to django...@googlegroups.com
Hallöchen!

Tim Shaffer writes:

> Why should an author instance ever return True for
> isinsnace(author_instance, Translator). That's not really the way
> Python or Django are designed to work.

Yes, but normally, you have tools like type(instance) or
instance.__class__. However, in Django, you don't. That's the
problem.

Besides, in Python,

for instance in instances:
instance.do_something()

calls the method of the respective class of "instance". However, in
Django,

for instance in RootClass.objects.all():
instance.do_something()

will *always* call RootClass.do_something(), which is almost never
what you want.

Tschö,
Torsten.

--
Torsten Bronger, aquisgrana, europa vetus
Jabber ID: torsten...@jabber.rwth-aachen.de
or http://bronger-jmp.appspot.com

Tim Shaffer

unread,
Apr 21, 2010, 9:15:38 AM4/21/10
to Django users
On Apr 21, 8:25 am, Torsten Bronger <bron...@physik.rwth-aachen.de>
wrote:
> Yes, but normally, you have tools like type(instance) or
> instance.__class__.  However, in Django, you don't.  That's the
> problem.

Sure you do...

>>> from website.blog.models import Entry
>>> e = Entry.objects.all()[0]
>>> e.__class__
<class 'website.blog.models.Entry'>
>>> type(e)
<class 'website.blog.models.Entry'>

>
> Besides, in Python,
>
> for instance in instances:
>     instance.do_something()
>
> calls the method of the respective class of "instance".  However, in
> Django,
>
> for instance in RootClass.objects.all():
>     instance.do_something()
>
> will *always* call RootClass.do_something(), which is almost never
> what you want.
>

I don't necessarily think that's a problem at all. That's the way it
should work. You want to be able to call RootClass.do_something() on
an instance of RootClass and have it run that method from one of the
sub classes?

Tom Evans

unread,
Apr 21, 2010, 9:28:13 AM4/21/10
to django...@googlegroups.com
On Wed, Apr 21, 2010 at 2:15 PM, Tim Shaffer <t...@tim-shaffer.com> wrote:
> On Apr 21, 8:25 am, Torsten Bronger <bron...@physik.rwth-aachen.de>
> wrote:
>> calls the method of the respective class of "instance".  However, in
>> Django,
>>
>> for instance in RootClass.objects.all():
>>     instance.do_something()
>>
>> will *always* call RootClass.do_something(), which is almost never
>> what you want.
>>
>
> I don't necessarily think that's a problem at all. That's the way it
> should work. You want to be able to call RootClass.do_something() on
> an instance of RootClass and have it run that method from one of the
> sub classes?
>

Yes please; this is called 'polymorphism', and is quite a neat
feature. If I were to run:

instance = Derived()
instance.func()

then I would want Derived.func to be executed. If, however, I wrote this:

instance = Derived()
Base.func(instance)

then I would expect Base.func to be executed.

Python doesn't really support 'true' polymorphism anyway, since
instance is always a 'Derived', there is no way to cast or represent
instance as a 'Base', so there is no late-binding (or rather, there is
only late binding).

On the other hand, there is no need, duck typing means there is no
need for objects that are used in exactly the same way being even
being slightly related - take django middleware as an example. They
all implement roughly the same interface, but are not related by type
to any other middleware*.


Cheers

Tom


* OK, so they mostly all derive from object..

Tim Shaffer

unread,
Apr 21, 2010, 9:35:54 AM4/21/10
to Django users
On Apr 21, 9:28 am, Tom Evans <tevans...@googlemail.com> wrote:
> On Wed, Apr 21, 2010 at 2:15 PM, Tim Shaffer <t...@tim-shaffer.com> wrote:
> > On Apr 21, 8:25 am, Torsten Bronger <bron...@physik.rwth-aachen.de>
> > wrote:
> >> calls the method of the respective class of "instance".  However, in
> >> Django,
>
> >> for instance in RootClass.objects.all():
> >>     instance.do_something()
>
> >> will *always* call RootClass.do_something(), which is almost never
> >> what you want.
>
> > I don't necessarily think that's a problem at all. That's the way it
> > should work. You want to be able to call RootClass.do_something() on
> > an instance of RootClass and have it run that method from one of the
> > sub classes?
>
> Yes please; this is called 'polymorphism', and is quite a neat
> feature. If I were to run:
>
> instance = Derived()
> instance.func()
>
> then I would want Derived.func to be executed. If, however, I wrote this:
>
> instance = Derived()
> Base.func(instance)
>
> then I would expect Base.func to be executed.

I think I must be missing something, because I'm pretty sure Django
doesn't change any of that.

Given the following example....

class Author(models.Model):
first = models.CharField(u'First Name', max_length=20)
last = models.CharField(u'Last Name', max_length=20)

def do_something(self):
print "I'm going to write"

class Translator(Author):
language = models.CharField(max_length=10)

def do_something(self):
print "I'm going to translate"

class Editor(Author):
language = models.CharField(max_length=10)

def do_something(self):
print "I'm going to edit"

>>> a = Author(first="Tim", last="Shaffer")
>>> a.translator = Translator(language="English")
>>> a.editor = Editor(language="German")

>>> t = Translator.objects.all()[0]

This works as I would expect it to...

>>> a.do_something()
"I'm going to write"

>>> t.do_something()
"I'm going to translate"

>>> Author.do_something(t)
"I'm going to write"

Calling a.do_something() should *never* call either
Translator.do_something() or Editor.do_something()

Torsten Bronger

unread,
Apr 21, 2010, 10:07:07 AM4/21/10
to django...@googlegroups.com
Hallöchen!

Tim Shaffer writes:

> [...]
>
>>>> a = Author(first="Tim", last="Shaffer")
>>>> a.translator = Translator(language="English")
>>>> a.editor = Editor(language="German")
>
>>>> t = Translator.objects.all()[0]
>
> This works as I would expect it to...
>
>>>> a.do_something()
> "I'm going to write"
>
>>>> t.do_something()
> "I'm going to translate"
>
>>>> Author.do_something(t)
> "I'm going to write"
>
> Calling a.do_something() should *never* call either
> Translator.do_something() or Editor.do_something()

You're right that this would contradict Django's documented
behaviour, which is efficient and well-defined. However, it is not
practical in presence of abstract base classes.

Consider the following scenario:

Author, Editor, and Translator are all derived from Person. No
person actually is "Person". "Person" is simply the common base
class. Then you want to list all contributors on a web page in a
table. The first column is the name -- that simple, because it is
an attribute. The second column is the result of "do_something()".
This wouldn't work, because the dynamic binding doesn't work in
Django:

{% for person in persons %}
<tr><td>{{ person.name }}</td><td>{{ person.do_something }}</td></tr>
{% endfor %}

My use case is somewhat different, by the way: I'd like to apply a
specially-tailored template to the instance whose name is derived
from the instance's model name. This doesn't work in vanilla
Django.

Mostly, though, you are only interested in the base class
attributes, so Django's behaviour is okay. Additionally, fetching
the actual instance for each instance is costly.

The cleanest solution is a contenttype attribute which contains the
actual model.

Tschö,
Torsten.

--
Torsten Bronger, aquisgrana, europa vetus
Jabber ID: torsten...@jabber.rwth-aachen.de
or http://bronger-jmp.appspot.com

Tom Evans

unread,
Apr 21, 2010, 10:05:40 AM4/21/10
to django...@googlegroups.com
You are absolutely right, its just that django with MTI adds the
possibility to get a reference to the base class as a valid object.

In raw python, an object that is a Derived is always a Derived, you
would never construct it as a Base.
In django, you can quite easily create an instance of Base that
actually represents a record of a Derived in the database.

For instance, if you had something like this:

class Playlist(models.Model):
media = models.ManyToManyField('Media')

class Media(models.Model):
date_added = models.DateField()
def play(self):
raise NotSupportedException

class MP3Media(Media):
file = models.CharField()
def play(self):
# do stuff

Then you would typically like to get the media and play it:

for media in playlist.media_set.all():
media.play()

However this would get the base Media class, which cannot be played.
The snippets posted above by Torsten allow a simple function to turn
Base into Derived in a situation like this.

This can mostly be worked around with generic foreign keys, which is
the only sane way of handling this IMO.

Cheers

Tom

Tim Shaffer

unread,
Apr 21, 2010, 12:09:24 PM4/21/10
to Django users
I think then you will run into a problem with you add another media
type:

class WAVMedia(Media):
file = models.CharField()
def play(self):
# do stuff

>>> m = Media()
>>> m.wavmedia = WAVMedia()
>>> m.mp3media = MP3Media()
>>> playlist = Playlist
>>> playlist.media_set.append(m)

for media in playlist.media_set.all():
media.play()

Should media.play() call play() on the Media class, WAVMedia class, or
the MP3Media class? It *should* call it on the Media class, which it
does, since you have an instance of Media. And even you wanted it to
call the play method on MP3Media or WAVMedia, how would you determine
which one to call? But this is the way multiple table inheritance is
supposed to work. This is the way python works:

class Media(object):
pass

class MP3Media(object):
def play(self):
# pass

If I have the following list:

songs = [ Media(), Media(), Media() ]

I would never expect to be able to call play() on any instance in that
list. Why would Django be any different?

I think in both these cases (there is no person actually an instance
of "Person", and there should be no actual instance of "Media"), it
would be best to mark the parent class abstract and use a generic
relation (like you said) to join to either MP3Media or WAVMedia. This
is the "Django way" and I believe is the functionality you're looking
for.

class Media(models.Model):
filename = models.CharField(max_length=200)
class Meta:
abstract = True

class MP3Media(Media):
def play(self):
print "playing mp3"

def __unicode__(self):
return self.filename

class WAVMedia(Media):
def play(self):
print "playing wav"

def __unicode__(self):
return self.filename

class Playlist(models.Model):
name = models.CharField(max_length=200)

class Song(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey() # WAVMedia or
MP3Media

playlist = models.ForeignKey(Playlist)

>>> m = MP3Media(filename="test.mp3")
>>> w = WAVMedia(filename="test.wav")
>>> p = Playlist(name="My Songs")
>>> p.song_set.create(content_object=w)
>>> p.song_set.create(content_object=m)
>>> for song in p.song_set.all():
... song.content_object.play()
playing mp3
playing wav

Tim Shaffer

unread,
Apr 21, 2010, 12:12:09 PM4/21/10
to Django users
Sorry, "class MP3Media(object):" should have been class
"MP3Media(Media):"

Torsten Bronger

unread,
Apr 21, 2010, 12:38:08 PM4/21/10
to django...@googlegroups.com
Hallöchen!

Tim Shaffer writes:

> [...]
>
> I think in both these cases (there is no person actually an
> instance of "Person", and there should be no actual instance of
> "Media"), it would be best to mark the parent class abstract and
> use a generic relation (like you said) to join to either MP3Media
> or WAVMedia. This is the "Django way" and I believe is the
> functionality you're looking for.

There is no need to mark it as "abstract". Besides, you lose the
possibility to link to it in ForeignKeys (which I need).

> [...]
You may do so, but this is not particularly simple. Note that if
this was implemented in pure Python code, you wouldn't need any
"Song" class. You would just create a list and throw different
objects into it. Even in C++, where all elements in a list must
have the same base class, which is therefore closer to Django's
constraints, you wouldn't need "Song".

The root problem is that Django stores too little in parent
classes. It stores the primary key, that's all. Then, it looks in
child tables where it can find the primary key there too, and if so,
you get the actual instance.

I think Django should store the fully qualified class name of each
row in the parent table. For example, the "Person" table would have
a column with entries like "myapp.Author" or "myapp.Translator".
Then, a find_actual_instance method could be implemented both
reliably and efficiently.

Tschö,
Torsten.

--
Torsten Bronger, aquisgrana, europa vetus
Jabber ID: torsten...@jabber.rwth-aachen.de
or http://bronger-jmp.appspot.com

Tom Evans

unread,
Apr 21, 2010, 1:02:04 PM4/21/10
to django...@googlegroups.com
On Wed, Apr 21, 2010 at 5:09 PM, Tim Shaffer <t...@tim-shaffer.com> wrote:
> I think then you will run into a problem with you add another media
> type:
>
> class WAVMedia(Media):
>  file = models.CharField()
>  def play(self):
>    # do stuff
>
>>>> m = Media()
>>>> m.wavmedia = WAVMedia()
>>>> m.mp3media = MP3Media()
>>>> playlist = Playlist
>>>> playlist.media_set.append(m)
>
> for media in playlist.media_set.all():
>  media.play()
>
> Should media.play() call play() on the Media class, WAVMedia class, or
> the MP3Media class? It *should* call it on the Media class, which it
> does, since you have an instance of Media. And even you wanted it to
> call the play method on MP3Media or WAVMedia, how would you determine
> which one to call? But this is the way multiple table inheritance is
> supposed to work. This is the way python works:

Well, no clearly not - those are all base instances. The point is that
polymorphism, forget python or django for a second, a standard feature
of most programming languages would say that if you get a Derived type
addressed as a Base type, then calling a virtual function on it would
call the Derived method.

Python sorts this out by not allowing you to alias a Derived type as a
Base type, except explicitly by calling the Base type as a class
function and passing in a Derived instance as 'self'. If you create an
instance of Base, its not a Derived - ever!

With Django's MTI, you *can* get a Derived type aliased as a Base
type, due to the way MTI works. This isn't a problem (it certainly
isn't one for me!), it's just something to be aware of. The OP wasn't,
and that's why we're having this discussion!

>
> class Media(object):
>    pass
>
> class MP3Media(object):
>    def play(self):
>        # pass
>
> If I have the following list:
>
> songs = [ Media(), Media(), Media() ]
>
> I would never expect to be able to call play() on any instance in that
> list. Why would Django be any different?



>
> I think in both these cases (there is no person actually an instance
> of "Person", and there should be no actual instance of "Media"), it
> would be best to mark the parent class abstract and use a generic
> relation (like you said) to join to either MP3Media or WAVMedia. This
> is the "Django way" and I believe is the functionality you're looking
> for.

No, it really isn't. I've obviously missed off a whole slew of things
from this example model, but there are a HOST of common options for a
Media class that are generic and non-specific to a type of Media. Not
storing this homogeneously would be a bit crap, don't you think? You
wouldn't be able to find media with a playlength more than 5 minutes,
only MP3Media, or WAVMedia, or....

As I mentioned, GFKs are the way to achieve the solution, which does
do everything needed, there's just no need to go to abstract
inheritance. I'd alter your models to not use abstract inheritance,
add a GFK on Media to point at the specific instance of the media:

class Media(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
media = generic.GenericForeignKey()

class MP3Media(Media):
def play(self):
print "playing mp3"

class WAVMedia(Media):
def play(self):
print "playing wav"

class Playlist(models.Model):
media = models.ManyToManyField('Media')

for m in my_playlist.media_set.all():
m.media.play()

This is what I was trying to say in my previous reply ;)

Cheers

Tom

Tom Evans

unread,
Apr 21, 2010, 1:07:29 PM4/21/10
to django...@googlegroups.com
On Wed, Apr 21, 2010 at 5:38 PM, Torsten Bronger
<bro...@physik.rwth-aachen.de> wrote:
> I think Django should store the fully qualified class name of each
> row in the parent table.  For example, the "Person" table would have
> a column with entries like "myapp.Author" or "myapp.Translator".
> Then, a find_actual_instance method could be implemented both
> reliably and efficiently.
>
> Tschö,
> Torsten.
>

This is effectively what contenttypes and generic foreign keys do. A
GFK has two parts, an item id, and a foreign key to the content type
model, which is a register of all django models, allowing you to
easily construct a Derived from a Base reference. See the end of my
reply to Tim and the docs:

http://docs.djangoproject.com/en/1.1/ref/contrib/contenttypes/#id1

The docs talk about a TaggedItem, for generating a faddish tag cloud,
but it works much better if the models you link to are actually
related and implement a common interface :)

Cheers

Tom

Tom Evans

unread,
Apr 21, 2010, 2:26:20 PM4/21/10
to django...@googlegroups.com
On Wed, Apr 21, 2010 at 6:59 PM, Torsten Bronger
<bro...@physik.rwth-aachen.de> wrote:
> But I think that it's not too expensive if by default Django stored
> both the PK and the actual model name in the parent table.
> Additionally, find_actual_instance should be built-in based on this
> additional column.
>
> After all, I've read about this problem for the forth time on this
> list (and I don't read it regularly), plus a private email
> conversion with someone who found old postings of me on Google
> Groups.
>
> Tschö,
> Torsten.
>

+1
In fact it wouldn't be too tricky to auto add those fields. A project
for the weekend, I think.

Torsten Bronger

unread,
Apr 21, 2010, 1:59:03 PM4/21/10
to django...@googlegroups.com
Hallöchen!

Tom Evans writes:

> On Wed, Apr 21, 2010 at 5:38 PM, Torsten Bronger
> <bro...@physik.rwth-aachen.de> wrote:
>
>> I think Django should store the fully qualified class name of
>> each row in the parent table.  For example, the "Person" table
>> would have a column with entries like "myapp.Author" or
>> "myapp.Translator". Then, a find_actual_instance method could be
>> implemented both reliably and efficiently.
>
> This is effectively what contenttypes and generic foreign keys
> do. A GFK has two parts, an item id, and a foreign key to the
> content type model, which is a register of all django models,
> allowing you to easily construct a Derived from a Base
> reference. See the end of my reply to Tim and the docs:
>
> http://docs.djangoproject.com/en/1.1/ref/contrib/contenttypes/#id1

Right, and I said that contenttypes are the way to go in my first
posting.

But I think that it's not too expensive if by default Django stored
both the PK and the actual model name in the parent table.
Additionally, find_actual_instance should be built-in based on this
additional column.

After all, I've read about this problem for the forth time on this
list (and I don't read it regularly), plus a private email
conversion with someone who found old postings of me on Google
Groups.

Tschö,
Torsten.

--
Torsten Bronger, aquisgrana, europa vetus
Jabber ID: torsten...@jabber.rwth-aachen.de
or http://bronger-jmp.appspot.com

Lachlan Musicman

unread,
Apr 21, 2010, 11:34:41 PM4/21/10
to django...@googlegroups.com
On Wed, Apr 21, 2010 at 19:33, Daniel Roseman <dan...@roseman.org.uk> wrote:
>>
>> Any tips?
>
> This is the expected behaviour. You've asked for an Author object, so
> that's what you've got. There's no way to tell from the Author model
> that this particular instance of it is also an instance of Translator.

I presume this will make you all wince, but while appreciating the
discussion I've caused I've solved it thusly:
------------------------
views.py
def index(request):
all_authors = Author.objects.all()
all_origAuthors = []
all_translators = Translators.objects.all()
for author in all_authors:
if author in all_translators:
pass
else:
all_origAuthors.append(author)
return render_to_response('books/index.html',locals())
------------------------

That nested loop is pretty inefficient, but it's a low visit site and
it shouldn't matter too much.

The other piece of advice I got was that I should have created a
second subclass:

class Author(Models.model) - this class _could_ be abstracted in this change.

class Writer(Author) - the Original Author
class Translator(Author)

Whether Author is abstract or not, we don't create any Author objects
directly, only Writer and Translator. Then it's easier :)

cheers
L.

David De La Harpe Golden

unread,
Apr 23, 2010, 7:05:01 AM4/23/10
to django...@googlegroups.com
On 22/04/10 04:34, Lachlan Musicman wrote:

> I presume this will make you all wince, but while appreciating the
> discussion I've caused I've solved it thusly:
> ------------------------
> views.py
> def index(request):
> all_authors = Author.objects.all()
> all_origAuthors = []
> all_translators = Translators.objects.all()
> for author in all_authors:
> if author in all_translators:
> pass
> else:
> all_origAuthors.append(author)
> return render_to_response('books/index.html',locals())
> ------------------------
>

Haven't really been following this thread, but taken in isolation:

Translator is a multi-table-inheritance subclass of Author, and
that means its primary key is a foreign key back to author's
primary key (at least in the way django presently implements things), so
e.g. the following is way to give authors who aren't translators:

Author.objects.exclude(pk__in=Translator.objects.all().values('pk'))
Reply all
Reply to author
Forward
0 new messages