Overriding __init__ and calling method defined in a subclass...

28 views
Skip to first unread message

Mattias Brändström

unread,
Feb 16, 2010, 5:48:11 PM2/16/10
to django-polymorphic
Hello!

Perhaps what I'm trying to do is a bit exotic. I'm not sure... :-)

Anyway, the following code snippet should show what I'm trying to do:

class Foo(models.Model):
foo = models.CharField(max_length=100)

def __init__(self, *args, **kwargs):
kwargs['foo'] = self.x()
super(Foo, self).__init__(*args, **kwargs)

class FooSubclass(Foo):
def x(self):
return 'ABC'

class Bar(PolymorphicModel):
bar = models.CharField(max_length=100)

def __init__(self, *args, **kwargs):
kwargs['bar'] = self.x()
super(Bar, self).__init__(*args, **kwargs)

class BarSubclass(Bar):
def x(self):
return 'XYZ'

The first pair of classes, Foo and FooSublass, works for me. I can
create instances of FooSublass and all those instances have 'ABC' in
insance.foo. The second pair of classes, using Polymorfic model, does
not work. This is what happens when I try to create a BarSubclass:

>>> BarSubclass.objects.create(foo='123')
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/usr/lib/python2.5/site-packages/django/db/models/manager.py",
line 126, in create
return self.get_query_set().create(**kwargs)
File "/usr/lib/python2.5/site-packages/django/db/models/query.py",
line 284, in create
obj = self.model(**kwargs)
File "/home/brasse/ubet/www/ubetsite/../ubetsite/ubetapp/models.py",
line 285, in __init__
kwargs['bar'] = self.x()
File "/home/brasse/ubet/www/ubetsite/polymorphic/polymorphic.py",
line 682, in __getattribute__
model = self.__class__.sub_and_superclass_dict.get(name, None)
AttributeError: type object 'BarSubclass' has no attribute
'sub_and_superclass_dict'

Is this a bug? If so is there an easy fix that can be applied?

Regards,
Mattias

Mattias Brändström

unread,
Feb 17, 2010, 3:22:32 AM2/17/10
to django-polymorphic

OK, I have done some thinking that I should have done before I posted
last night. If I call the ctor of my superclass before I start
accessing attributes in the subclass instance things work much better.
Making this change to the subclass ctor is OK in my case and probably
many more scenarios. This is what my toy example look like now:

class Bar(PolymorphicModel):
baz = models.CharField(max_length=100)
def __init__(self, *args, **kwargs):
super(Bar, self).__init__(*args, **kwargs)
self.baz = self.x()

class BarSubclass(Bar):
def x(self):
return 'XYZ'

But now some new problems arise:

>>> from ubetsite.ubetapp.models import *
>>> o = BarSubclass(baz='123')
>>> o.save()
>>> for o in BarSubclass.objects.all(): print o.baz
...
XYZ
>>> for o in Bar.objects.all(): print o.baz
...


Traceback (most recent call last):
File "<console>", line 1, in <module>

File "/usr/lib/python2.5/site-packages/django/db/models/query.py",
line 93, in _result_iter
self._fill_cache()
File "/usr/lib/python2.5/site-packages/django/db/models/query.py",
line 644, in _fill_cache
self._result_cache.append(self._iter.next())
File "/home/brasse/ubet/www/ubetsite/polymorphic/polymorphic.py",
line 235, in iterator
try: base_result_objects.append(base_iter.next())
File "/usr/lib/python2.5/site-packages/django/db/models/query.py",
line 221, in iterator
obj = self.model(*row[index_start:aggregate_start])
File "/home/brasse/ubet/www/ubetsite/../ubetsite/ubetapp/models.py",
line 280, in __init__
self.baz = self.x()
File "/home/brasse/ubet/www/ubetsite/polymorphic/polymorphic.py",
line 687, in __getattribute__
return super(PolymorphicModel, self).__getattribute__(name)
AttributeError: 'Bar' object has no attribute 'x'

Accessing the objects via Bar does not work. It would be very nice if
this could work. Any ideas? Can this be made to work? I might be able
to have a look at it. Any pointers on where in the code I should start
poking around?

Regards,
Mattias

Bert Constantin

unread,
Feb 18, 2010, 9:56:53 AM2/18/10
to django-polymorphic, theb...@gmail.com

> Perhaps what I'm trying to do is a bit exotic. I'm not sure... :-)

Your example looks perfectly valid to me.

> Is this a bug? If so is there an easy fix that can be applied?

Yes, this is a bug in PolymorphicModel's __getattribute__. There is a
problem if model attributes (like x) are accessed before the
superclass __init__ is called. I'll post the fix here shortly (and
update the repository).

I'll also look at your second report and see if I can fix this as
well.

Kind Regards,
Bert Constantin

Bert Constantin

unread,
Feb 18, 2010, 2:22:32 PM2/18/10
to django-polymorphic
> OK, I have done some thinking that I should have done before I posted last night.

I'm quite happy with your posting, as it demonstrated two issues with
django_polymorphic (which I could fix; the second was a problem with
field-name == model-name, as with bar and Bar). I will push an update
to GitHub & BitBucket during the next days.

Regarding the "AttributeError: 'Bar' object has no attribute 'x'":

The current implementation of django_polymorphic needs to be able to
instantiate base model objects (in
PolymorphicQueryset._get_real_instances). In your example,
django_polymorphic will do a query over Bar objects and try to
instantiate these (which fails). I believe Django internally also
needs to instantiate Base model objects for various purposes,
therefore modifying this in PolymorphicQueryset would most likely not
solve the issue.

But you could assign self.x() to self.baz only if baz is empty, like
so:

class Bar(PolymorphicModel):
baz = models.CharField(max_length=100)
def __init__(self, *args, **kwargs):
super(Bar, self).__init__(*args, **kwargs)

if not self.baz: self.baz = self.x()

If you want to set the initial value of baz depending on the sub-model
you created (BarSubclass in this case), then this looks like the best
way to accomplish this anyways.

Kind Regards,
Bert Constantin

Mattias Brändström

unread,
Feb 23, 2010, 4:48:21 PM2/23/10
to django-polymorphic
Nice! Thanks for fixing the "...has no attribute
'sub_and_superclass_dict'" bug!

On Feb 18, 8:22 pm, Bert Constantin <bert.constan...@gmx.de> wrote:
> > OK, I have done some thinking that I should have done before I posted last night.
>

> I'm quite happy with your posting, as it demonstrated two issues with
> django_polymorphic (which I could fix; the second was a problem with
> field-name == model-name, as with bar and Bar). I will push an update
> to GitHub & BitBucket during the next days.
>
> Regarding the "AttributeError: 'Bar' object has no attribute 'x'":
>
> The current implementation of django_polymorphic needs to be able to
> instantiate base model objects (in
> PolymorphicQueryset._get_real_instances). In your example,
> django_polymorphic will do a query over Bar objects and try to
> instantiate these (which fails). I believe Django internally also
> needs to instantiate Base model objects for various purposes,
> therefore modifying this in PolymorphicQueryset would most likely not
> solve the issue.
>
> But you could assign self.x() to self.baz only if baz is empty, like
> so:
>
> class Bar(PolymorphicModel):
>     baz = models.CharField(max_length=100)
>     def __init__(self, *args, **kwargs):
>         super(Bar, self).__init__(*args, **kwargs)
>         if not self.baz: self.baz = self.x()
>
> If you want to set the initial value of baz depending on the sub-model
> you created (BarSubclass in this case), then this looks like the best
> way to accomplish this anyways.
>

I have been playing around with my code a bit more and I think the
approach you mention above is the way to do it.

It has been a while since I played around with an ORM. But I think
that my current take on overloading __init__ is that it should
probably be avoided. I will move the things I did in __init__ to
factory functions instead.

Regards,
Mattias

Reply all
Reply to author
Forward
0 new messages