Как построить witt django-mptt запрос, присоединяющийся к любому потомку элемента иерархии mptt?

Я хочу построить с помощью django-mptt иерархию классов овощей (Фрукты -> Ягоды -> Клубника -> Клубника Дугласа), являющуюся последним уровнем разнообразия. Запасы розничных продавцов часто связаны с уровнем листа, сортом (Золотое яблоко, клубника Дугласа...), но потребители привыкли спрашивать о верхнем уровне (яблоко, груша, апельсин, салат...). Таким образом, полезным запросом будет «выбрать всех розничных продавцов, у которых в наличии есть продукт «Клубника» или какие-либо дети.

Позвольте мне проиллюстрировать это на примере кода:

class Vegetable(MPTTModel) :
    name = TextField(...)
    group = TreeForeignKey('self',
        related_name='subgroups',
        null=True, default=None)
    class MPTTMeta:
        parent_attr = 'group'

    def getRetailers(self) :
        # Here the missing code

class Retailer(Model) :
    name = TextField(...)
    vegetables = ManyToMany(Vegetable, null=True)

fruits = Vegetable(name='Fruit')
pears = Vegetable(name='Pear', group=fruits)
apples = Vegetable(name='Apple', group=fruits)
goldenApples = Vegetable(name='Golden Apple', group=apples)
royalApples = Vegetable(name='Royal Apple', group=apples)
for o in fruits, pears, apples, goldenApples, royalApples : o.save()

toni = Retailer(name='Toni')
pere = Retailer(name='Pere')
benet = Retailer(name='Benet')
mall = Retailer(name='CityMall')
for o in toni, pere, benet, mall : o.save()

toni.vegetables.add(pears)
pere.vegetables.add(goldenApple)
pere.vegetables.add(royalApple)
benet.vegetables.add(goldenApple)
mall.vegetables.add(apples)

# The following query set should return Pere, Benet and CityMall but not Toni.
Vegetable.get(name='Apple').getRetailers()

Итак, как мне создать такой запрос с помощью Django API?


person vokimon    schedule 10.09.2014    source источник
comment
бесполезный комментарий программиста-интроверта: на самом деле клубника — не ягода. :D   -  person Hamish    schedule 11.09.2014
comment
Малина не является настоящей ягодой, но коммерциализируется как таковая. Точно так же помидоры и баклажаны являются настоящими фруктами, но они не коммерциализируются как таковые.   -  person vokimon    schedule 11.09.2014
comment
Я знаю, это была неудачная шутка. Надеюсь, мой реальный ответ помог :)   -  person Hamish    schedule 13.09.2014


Ответы (1)


Вы можете получить набор запросов всех потомков MPTTModel с помощью get_descendants:

Создает QuerySet, содержащий потомков этого экземпляра модели, в порядке дерева.

Если include_self равно True, QuerySet также будет включать этот экземпляр модели.

Итак, для конкретного овоща:

# get retailers where...
retailers = Retailers.objects.filter(
    # ...one or more of it's vegetables are in the list of
    # all things descendant of 'vegetable', including itself.
    vegetables__in=vegetable.get_descendants(include_self=True)
).distinct()
person Hamish    schedule 10.09.2014
comment
Круто, работает! Мне просто нужно было добавить .distinct() в запрос, чтобы удалить дубликаты, например, если у продавца есть три разных сорта яблок. овощи__в=vegetable.get_descendants(include_self=True).distinct() - person vokimon; 14.09.2014
comment
В коде моего предыдущего комментария есть синтаксическая ошибка: retailers = retailers.objects.filter(vegetables__in=vegetable.get_descendants(include_self=True)).distinct() - person vokimon; 14.09.2014