Django DB, encontrando Categorias cujos itens são todos em um subconjunto

votos
0

Eu tenho dois modelos:

class Category(models.Model):
    pass

class Item(models.Model):
    cat = models.ForeignKey(Category)

Eu estou tentando retornar todas as categorias para as quais todos os itens dessa categoria pertencem a um determinado subconjunto de itens de ids (graças fixo). Por exemplo, todas as categorias para as quais todos os itens associados a essa categoria tem ids no conjunto [1,3,5].

Como isso pode ser feito usando a sintaxe de consulta do Django (a partir de 1.1 beta)? Idealmente, todo o trabalho deve ser feito no banco de dados.

Publicado 02/04/2009 em 08:03
fonte usuário
Em outras línguas...                            


3 respostas

votos
0

Category.objects.filter(item__id__in=[1, 3, 5])

Django cria o navio relação inversa com o modelo sem a chave estrangeira. Você pode filtrar-lo usando seu nome relacionado (geralmente apenas o nome do modelo em minúsculas, mas pode ser substituído manualmente), dois sublinhados, eo nome do campo que você deseja consultar on.

Respondeu 04/04/2009 em 23:59
fonte usuário

votos
0

Eu brinquei com isso um pouco. Se QuerySet.extra () aceitou um "ter" parâmetro Eu acho que seria possível fazê-lo no ORM com um pouco de SQL cru na cláusula HAVING. Mas isso não acontecer, então eu acho que você tem que escrever toda a consulta no SQL cru se quiser que o banco de dados fazendo o trabalho.

EDITAR:

Esta é a consulta que você recebe a maneira da parte lá:

from django.db.models import Count
Category.objects.annotate(num_items=Count('item')).filter(num_items=...)

O problema é que para a consulta para o trabalho, "..." precisa ser uma subconsulta correlacionada que olha para cima, para cada categoria, o número de seus itens em allowed_items. Se .extra tinha um "ter" argumento, você faria assim:

Category.objects.annotate(num_items=Count('item')).extra(having="num_items=(SELECT COUNT(*) FROM app_item WHERE app_item.id in % AND app_item.cat_id = app_category.id)", having_params=[allowed_item_ids])
Respondeu 02/04/2009 em 18:44
fonte usuário

votos
0

digamos que você exigir que todos os itens para estar no seguinte conjunto:

allowable_items = set([1,3,4])

Uma solução bruteforce seria verificar o item_set para cada categoria assim:

categories_with_allowable_items = [
    category for category in 
    Category.objects.all() if
    set([item.id for item in category.item_set.all()]) <= allowable_items
]

mas nós realmente não tem que verificar todas as categorias, como categories_with_allowable_items é sempre vai ser um subconjunto das categorias relacionadas a todos os itens com ids em allowable_items ... então isso é tudo que temos que verificar (e isto deve ser mais rápido):

categories_with_allowable_items = set([
    item.category for item in
    Item.objects.select_related('category').filter(pk__in=allowable_items) if
    set([siblingitem.id for siblingitem in item.category.item_set.all()]) <= allowable_items
])

Se o desempenho não é realmente um problema, então o último destes dois (se não o primeiro) deve ser fino. se estes são muito grandes tabelas, você pode ter que vir para cima com uma solução mais sofisticada. Também se você estiver usando uma versão particularmente antiga de python lembre-se que você terá que importar o módulo conjuntos

Respondeu 02/04/2009 em 11:24
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more