【问题标题】:Django multi-table inheritance, how to know which is the child class of a model?Django多表继承,如何知道模型的子类是哪个?
【发布时间】:2012-09-06 20:01:31
【问题描述】:

我在 django 中遇到了多表继承问题。

让我们以银行账户为例。

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

现在,我想为帐户类型添加一个值,但我只有一个帐户对象,例如 acc=account.object.get(pk=29) 。那么, acc 的孩子是谁呢?

Django 自动在 accounttypeA 和 accounttypeB 中创建一个 account_ptr_id 字段。所以,我的解决方案是:

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…”)

此时也许这是一个绘图板问题! :)

我希望我在我的例子中已经很清楚了。谢谢

【问题讨论】:

    标签: python django inheritance subclass


    【解决方案1】:

    据我所知,没有 Django 内置的方法可以做到这一点。

    但是,给定acc=account.object.get(pk=29),您可以使用:

    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】:

    我的解决方案基于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 为类 account 添加了两个字段:accounttypeaaccounttypeb。如果您有 pk=42 的 accounttypeB 对象,您可以像这样从父级访问:

      account.objects.get(pk=42).accounttypeb
      >>> <accounttypeB instance>
      

      您可以将 CharField 添加到具有实际子模型的每个子模型的父模型,然后使用 getattr,如果有很多子模型(它可能比很多 try .. except xxx.DoesNotExist 块更好)。

      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)
      

      但是你必须使用accounttypeA.objects.create(...)accounttypeB.objects.create(...) 创建模型——否则这个技巧将不起作用。 (https://docs.djangoproject.com/en/1.5/topics/db/models/#multi-table-inheritance)

      【讨论】:

        【解决方案4】:

        您可以使用hasattr() 方法,例如:

        if hasattr(account, 'accounttypea'):
           account.accounttypea.<somefield> = <some value>
           do something here....
        
        elif hasattr(account, 'accounttypeb'):
           account.accounttypeb.<somefield> = <some value>
           do something here...
        

        它不是那么干燥但有效。 :)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-03-05
          • 1970-01-01
          • 1970-01-01
          • 2012-09-23
          • 2017-07-11
          • 1970-01-01
          • 1970-01-01
          • 2011-10-04
          相关资源
          最近更新 更多