问题
I'm having a problem with multi-table inheritance in django.
Let’s make an example with bank accounts.
class account(models.Model):
name = models……
class accounttypeA(account):
balance = models.float…..
def addToBalance(self, value):
self.balance += value
class accounttypeB(account):
balance = models.int…. # NOTE this
def addToBalance(self, value):
value = do_some_thing_with_value(value) # NOTE this
self.balance += value
Now, i want to add a value to an accounttype, but all i have is an account object, for instance acc=account.object.get(pk=29) . So, who is the child of acc ?
Django automatically creates an account_ptr_id field in accounttypeA and accounttypeB. So, my solution was:
child_class_list = ['accounttypeA', 'accounttypeB']
for cl in child_class_list:
try:
exec(“child = ” + str(cl) + “.objects.select_for_update().get(account_ptr_id=” + str(acc.id) + “)”)
logger.debug(“Child found and ready to use.”)
return child
except ObjectDoesNotExist:
logger.debug(“Object does not exist, moving on…”)
Maybe it's a drawing board problem at this point! :)
I hope I have been clear in my example. Thanks
回答1:
To the best of my knowledge there isn't a Django built-in way to do this.
However, given acc=account.object.get(pk=29), you can use:
try:
typeA = acc.accounttypeA
# acc is typeA
except accounttypeA.DoesNotExist:
# acc should be typeB if account only has typeA and typeB subclasses
try:
typeB = acc.accounttypeB
# acc is typeB
except accounttypeB.DoesNotExist:
# acc should be typeA if account only has typeA and typeB subclasses
回答2:
my solution was based on this
class account(models.Model):
name = models……
def cast(self):
"""
This method is quite handy, it converts "self" into its correct child class. For example:
.. code-block:: python
class Fruit(models.Model):
name = models.CharField()
class Apple(Fruit):
pass
fruit = Fruit.objects.get(name='Granny Smith')
apple = fruit.cast()
:return self: A casted child class of self
"""
for name in dir(self):
try:
attr = getattr(self, name)
if isinstance(attr, self.__class__) and type(attr) != type(self):
return attr
except:
pass
@staticmethod
def allPossibleAccountTypes():
#this returns a list of all the subclasses of account (i.e. accounttypeA, accounttypeB etc)
return [str(subClass).split('.')[-1][:-2] for subClass in account.__subclasses__()]
def accountType(self):
try:
if type(self.cast()) == NoneType:
#it is a child
return self.__class__.__name__
else:
#it is a parent, i.e. an account
return str(type(self.cast())).split('.')[-1][:-2]
except:
logger.exception()
accountType.short_description = "Account type"
class accounttypeA(account):
balance = models.float…..
def addToBalance(self, value):
self.balance += value
class accounttypeB(account):
balance = models.int…. # NOTE this
回答3:
Django adds to class account two fields: accounttypea and accounttypeb. If you have accounttypeB object with pk=42, you can access from parent like this:
account.objects.get(pk=42).accounttypeb
>>> <accounttypeB instance>
You can add CharField to parent model with actual child-type for every child, and then use getattr, if there are a lot of child models (it may be better than a lot of try .. except xxx.DoesNotExist blocks).
class account(models.Model):
name = models……
cls = CharField(...)
def ext(self):
return getattr(self, self.cls.lower())
class accounttypeA(account):
balance = models.float…..
def addToBalance(self, value):
self.balance += value
class accounttypeB(account):
balance = models.int…. # NOTE this
def addToBalance(self, value):
value = do_some_thing_with_value(value) # NOTE this
self.balance += value
# example
accounttypeB.objects.create(balance=10, name='Vincent Law', cls="accounttypeB")
accounttypeA.objects.create(balance=9.5, name='Re-l Mayer', cls="accounttypeA")
for obj in account.objects.all():
obj.ext().addToBalance(1.0)
print(obj.name, obj.cls)
but you MUST create models using accounttypeA.objects.create(...) and accounttypeB.objects.create(...) - or else this trick will not work.
(https://docs.djangoproject.com/en/1.5/topics/db/models/#multi-table-inheritance)
回答4:
You can use hasattr() method like:
if hasattr(account, 'accounttypea'):
account.accounttypea.<somefield> = <some value>
do something here....
elif hasattr(account, 'accounttypeb'):
account.accounttypeb.<somefield> = <some value>
do something here...
It's not so DRY but works. :)
来源:https://stackoverflow.com/questions/12411232/django-multi-table-inheritance-how-to-know-which-is-the-child-class-of-a-model