How do I access the child classes of an object in

2019-01-01 10:23发布

In Django, when you have a parent class and multiple child classes that inherit from it you would normally access a child through parentclass.childclass1_set or parentclass.childclass2_set, but what if I don't know the name of the specific child class I want?

Is there a way to get the related objects in the parent->child direction without knowing the child class name?

8条回答
像晚风撩人
2楼-- · 2019-01-01 10:40

You can use django-polymorphic for that.

It allows to automatically cast derived classes back to their actual type. It also provides Django admin support, more efficient SQL query handling, and proxy model, inlines and formset support.

The basic principle seems to be reinvented many times (including Wagtail's .specific, or the examples outlined in this post). It takes more effort however, to make sure it doesn't result in an N-query issue, or integrate nicely with the admin, formsets/inlines or third party apps.

查看更多
大哥的爱人
3楼-- · 2019-01-01 10:41

In Python, given a ("new-style") class X, you can get its (direct) subclasses with X.__subclasses__(), which returns a list of class objects. (If you want "further descendants", you'll also have to call __subclasses__ on each of the direct subclasses, etc etc -- if you need help on how to do that effectively in Python, just ask!).

Once you have somehow identified a child class of interest (maybe all of them, if you want instances of all child subclasses, etc), getattr(parentclass,'%s_set' % childclass.__name__) should help (if the child class's name is 'foo', this is just like accessing parentclass.foo_set -- no more, no less). Again, if you need clarification or examples, please ask!

查看更多
高级女魔头
4楼-- · 2019-01-01 10:42

Here's my solution, again it uses _meta so isn't guaranteed to be stable.

class Animal(models.model):
    name = models.CharField()
    number_legs = models.IntegerField()
    ...

    def get_child_animal(self):
        child_animal = None
        for r in self._meta.get_all_related_objects():
            if r.field.name == 'animal_ptr':
                child_animal = getattr(self, r.get_accessor_name())
        if not child_animal:
            raise Exception("No subclass, you shouldn't create Animals directly")
        return child_animal

class Dog(Animal):
    ...

for a in Animal.objects.all():
    a.get_child_animal() # returns the dog (or whatever) instance
查看更多
不流泪的眼
5楼-- · 2019-01-01 10:56

It turns out that what I really needed was this:

Model inheritance with content type and inheritance-aware manager

That has worked perfectly for me. Thanks to everyone else, though. I learned a lot just reading your answers!

查看更多
看风景的人
6楼-- · 2019-01-01 10:56

You can achieve this looking for all the fields in the parent that are an instance of django.db.models.fields.related.RelatedManager. From your example it seems that the child classes you are talking about are not subclasses. Right?

查看更多
谁念西风独自凉
7楼-- · 2019-01-01 11:00

An alternative approach using proxies can be found in this blog post. Like the other solutions, it has its benefits and liabilities, which are very well put in the end of the post.

查看更多
登录 后发表回答