Django ORM, фильтрация объектов по типу с наследованием модели - PullRequest
1 голос
/ 19 сентября 2009

Итак, у меня есть две модели ...

Родитель и ребенок.

Ребенок расширяет Родителя.

Когда я делаю

Parent.objects.all (), я получаю и родителей, и детей.

Я хочу только родителей

Есть ли аргумент Parent.objects.filter (), который я могу использовать, чтобы получить только объекты Parent вместо объектов, расширяющих родительский объект?

Ответы [ 4 ]

13 голосов
/ 08 сентября 2010

Я нашел лучший способ решить эту проблему, используя ORM django и без необходимости каких-либо изменений в ваших моделях (например, ABC):

class Parent(models.Model):
    field1 = models.IntegerField()
    field2 = models.IntegerField()

class Child(Parent):
    field3 = models.IntegerField()

#Return all Parent objects that aren't also Child objects:
Parent.objects.filter(child=None)

Это приведет к следующему запросу (концептуальный, фактический запрос может отличаться):

ВЫБЕРИТЕ "ap_parent". "Field1", "ap_parent". "Field2" FROM "ap_parent" INNER ПРИСОЕДИНЯЙТЕСЬ к "ap_child" ON ("parent". "parent_ptr_id" = "ap_child". "parent_ptr_id") ГДЕ "ap_child". "parent_ptr_id" IS NULL

3 голосов
/ 20 сентября 2009

Может быть, это хорошее место, чтобы использовать A bstract B ase C , вместо использования наследования. Азбука содержит все поля, общие для ваших классов. Таким образом, в вашем случае у вас будет один определяемый ABC, который имеет ваш текущий родительский класс и 2 класса, которые будут наследоваться от ABC, которые соответствуют вашим родительским и дочерним классам.

class ABC(models.Model):
    field1 = models.CharField(max_length=200)
    field2 = models.CharField(max_length=100)
    ....

    class Meta:
        abstract = True

class Parent(ABC):
    ....

class Child(ABC):
    parent = models.ForeignKey(Parent)

Проверьте здесь для получения дополнительной информации: Модель наследования и Абстрактные базовые классы

2 голосов
/ 19 сентября 2009

Вы уверены, что наследование является правильным решением здесь? Как насчет этого?

class MyModel(models.Model):
    foo = models.IntegerField()
    parent = models.ForeignKey("self", null=True)

Затем вы можете запросить все объекты, которые являются родителями, как это:

parents = MyModel.objects.filter(parent__isnull=True)
children = MyModel.objects.filter(parent__isnull=False)

@ Алекс: фильтрация по типу не будет работать. Модель наследования Джанго не настолько богата. Например. с этими моделями:

class Parent(models.Model):
    foo = models.CharField(max_length=5)

class Child(Parent):
    bar = models.CharField(max_length=5)

Вы получаете это поведение:

In [1]: from myexample.models import Parent, Child

In [2]: p = Parent(foo='x')

In [3]: p.save()

In [4]: p2 = Parent(foo='y')

In [5]: p2.save()

In [6]: c1 = Child(bar='1', foo='a')

In [7]: c1.save()

In [8]: c2 = Child(bar='2', foo='b')

In [9]: c2.save()

In [10]: len(Parent.objects.all())
Out[10]: 4

In [11]: len([p for p in Parent.objects.all() if type(p) is Parent])
Out[11]: 4

In [12]: len(Child.objects.all())
Out[12]: 2
1 голос
/ 19 сентября 2009

Метод filter, по сути, предназначен для построения предложения WHERE в SQL-запросе, и это очень неудобное место, чтобы спорить о точных типах. А как же вместо этого ...:

(p for Parent.objects.all() if type(p) is Parent)

это итерация (используйте [ ] снаружи вместо ( ), если вам нужен список вместо) для всех объектов, которые точно типа Parent - подклассы не допускаются.

...