在基类上查询后确定 Django 模型实例类型

发布于 2024-10-20 21:05:59 字数 739 浏览 8 评论 0原文

在从基类的查询返回后,有没有办法确定 Django 数据库对象的“真实”类是什么?

例如,如果我有这些模型...

class Animal(models.Model):
    name= models.CharField(max_length=128)

class Person(Animal):
    pants_size = models.IntegerField(null=True)

class Dog(Animal):
    panting_rate = models.IntegerField(null=True)

并创建这些实例...

Person(name='Dave').save()
Dog(name='Mr. Rufflesworth').save()

如果我执行像 Animal.objects.all() 这样的查询,我最终会得到两个 Animal 实例,而不是 Person 实例和 Dog 实例。有什么方法可以确定哪个实例属于哪种类型?


仅供参考:我已经尝试过这样做......

isinstance(Animal.objects.get(name='Dave'),Person) # <-- Returns false!

但这似乎不起作用。

Is there a way to determine what the 'real' class of a Django database object is, after it has been returned from a query for on a base class?

For instance, if I have these models...

class Animal(models.Model):
    name= models.CharField(max_length=128)

class Person(Animal):
    pants_size = models.IntegerField(null=True)

class Dog(Animal):
    panting_rate = models.IntegerField(null=True)

And create these instances...

Person(name='Dave').save()
Dog(name='Mr. Rufflesworth').save()

If I do a query like Animal.objects.all(), I end up with two Animal instances, not an instance of Person and an instance of Dog. Is there any way to determine which instance is of which type?


FYI: I already tried doing this...

isinstance(Animal.objects.get(name='Dave'),Person) # <-- Returns false!

But that doesn't seem to work.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

暗地喜欢 2024-10-27 21:05:59

我们实现了自己的cast()函数,效果很好(没有ContentType):

class Base(models.Model):
    """
    If your class needs the basics, like created date, modified date etc, then
    inherit from this Base class.
    """
    created = models.DateTimeField(_('Created'), auto_now_add=True)
    modified = models.DateTimeField(_('Modified'), auto_now=True)

    class Meta:
        abstract = True

    def __str__(self):
        return '%s [%s]' % (self.__class__.__name__, self.id)

    def get_class_name(self):
        return str(self.__class__.__name__).lower()

    def to_json(self, include_related=True):
        return {
            'id': self.id,
            'created': self.created.isoformat(),
            'modified': self.modified.isoformat(),
            'class_name': self.__class__.__name__
        }

    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__):
                    return attr
            except:
                pass
        return self

We implemented our own cast() function that works quite well (Without ContentType's):

class Base(models.Model):
    """
    If your class needs the basics, like created date, modified date etc, then
    inherit from this Base class.
    """
    created = models.DateTimeField(_('Created'), auto_now_add=True)
    modified = models.DateTimeField(_('Modified'), auto_now=True)

    class Meta:
        abstract = True

    def __str__(self):
        return '%s [%s]' % (self.__class__.__name__, self.id)

    def get_class_name(self):
        return str(self.__class__.__name__).lower()

    def to_json(self, include_related=True):
        return {
            'id': self.id,
            'created': self.created.isoformat(),
            'modified': self.modified.isoformat(),
            'class_name': self.__class__.__name__
        }

    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__):
                    return attr
            except:
                pass
        return self
爱格式化 2024-10-27 21:05:59

我过去也遇到过类似的问题,最终找到了一个令人满意的解决方案,这要归功于这个答案

通过实现一个存储真实类的抽象类并由父类继承它,一次就可以将每个父类实例转换为实际类型。 (该答案中使用的抽象类现在可在

例如,一旦定义了抽象类(或者如果您有 django-model-utils),您可以简单地执行

class Animal(InheritanceCastModel):
    name= models.CharField(max_length=128)

class Person(Animal):
    pants_size = models.IntegerField(null=True)

class Dog(Animal):
    panting_rate = models.IntegerField(null=True)

操作 :琐碎的:

>>> from zoo.models import Animal, Person, Dog
>>> Animal(name='Malcolm').save()
>>> Person(name='Dave').save()
>>> Dog(name='Mr. Rufflesworth').save()
>>> for obj in Animal.objects.all():
...     print obj.name, type(obj.cast())
...
Malcolm <class 'zoo.models.Animal'>
Dave <class 'zoo.models.Person'>
Mr. Rufflesworth <class 'zoo.models.Dog'>

I had a similar problem in the past and eventually found a satisfactory solution thanks to this answer.

By implementing an abstract class that stores the real class and have it inherited by your parent class, once can cast each parent class instance to the actual type. (The abstract class used in that answer is now available in django-model-utils.)

For example, once you have the abstract class defined (or if you have django-model-utils), you can simply do:

class Animal(InheritanceCastModel):
    name= models.CharField(max_length=128)

class Person(Animal):
    pants_size = models.IntegerField(null=True)

class Dog(Animal):
    panting_rate = models.IntegerField(null=True)

Using it is trivial:

>>> from zoo.models import Animal, Person, Dog
>>> Animal(name='Malcolm').save()
>>> Person(name='Dave').save()
>>> Dog(name='Mr. Rufflesworth').save()
>>> for obj in Animal.objects.all():
...     print obj.name, type(obj.cast())
...
Malcolm <class 'zoo.models.Animal'>
Dave <class 'zoo.models.Person'>
Mr. Rufflesworth <class 'zoo.models.Dog'>
对风讲故事 2024-10-27 21:05:59

是的,这是可以完成的——您只需要查询自动反向 OneToOneField 关系。例如:

a = Animal.objects.select_related('person', 'dog')[0]
a = a.person or a.dog or a # whichever is not None
print a
print isinstance(a, Person)

这里使用 select_lated 允许在单个查询中完成此操作,而不必在访问子类属性/关系时测试 DoesNotExist 异常。

另请参阅我的答案此处InheritanceManager< /code> 在 django-model-utils 中以获得更优雅/长期的效果解决方案。

我们正在寻找在 Django 核心中使这一切变得更容易的方法。

Yes, this can be done -- you just need to query the automatic reverse OneToOneField relations. E.g.:

a = Animal.objects.select_related('person', 'dog')[0]
a = a.person or a.dog or a # whichever is not None
print a
print isinstance(a, Person)

The use of select_related here allows this to be done in a single query, rather than having to test for DoesNotExist exceptions when you access the subclass attributes/relations.

See also my answer here and the InheritanceManager in django-model-utils for a more elegant/long-term solution.

We're looking at ways of making this easier in Django's core.

月光色 2024-10-27 21:05:59

要解决此问题,请考虑使用 django-polymorphic。它支持继承模型的自动向下转换,与ForeignKeys/ManyToMany字段一起使用,并且也集成在管理中。

To solve this, consider using django-polymorphic. It supports automatic downcasting of inherited models, works with ForeignKeys/ManyToMany fields and integrates in the admin too.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文