Pergunta

Eu tenho a seguinte estrutura do modelo:

class Container(models.Model):
    pass

class Generic(models.Model):
    name = models.CharacterField(unique=True)
    cont = models.ManyToManyField(Container, null=True)
    # It is possible to have a Generic object not associated with any container, 
    # thats why null=True

class Specific1(Generic):
    ...

class Specific2(Generic):
    ...

...

class SpecificN(Generic):
    ...

Say, eu preciso recuperar todos os modelos do tipo Specific, que têm uma relação com um recipiente particular.

O SQL para que seja mais ou menos trivial, mas isso não é a questão. Infelizmente, eu não tenho muita experiência em trabalhar com ORMs (ORM do Django em particular), para que eu possa estar faltando um padrão aqui.

Quando feito de uma forma de força bruta, -

c = Container.objects.get(name='somename') # this gets me the container
items = c.generic_set.all() 
# this gets me all Generic objects, that are related to the container
# Now what? I need to get to the actual Specific objects, so I need to somehow
# get the type of the underlying Specific object and get it
for item in items:
    spec = getattr(item, item.get_my_specific_type())

Isso resulta em uma tonelada de visitas db (um para cada registro genérico, que se refere a um Container), de modo que este não é obviamente a maneira de fazê-lo. Agora, poderia, talvez, ser feito por obter o SpecificX objetos diretamente:

s = Specific1.objects.filter(cont__name='somename')
# This gets me all Specific1 objects for the specified container
...
# do it for every Specific type

dessa forma a db será atingida uma vez para cada tipo específico (aceitável, eu acho).

Eu sei, que .select_related () não trabalho com as relações M2M, por isso não é de muita ajuda aqui.

Para reiterar, o resultado final tem de ser uma coleção de SpecificX objetos (não genérico).

Foi útil?

Solução

Eu acho que você já delineou as duas possibilidades fáceis. Ou você faz uma consulta do filtro único contra genérico e, em seguida, lançar cada item para seu subtipo específico (resultados em n + 1 consultas, onde n é o número de itens retornados), ou você faz uma consulta separado contra cada tabela específica (resultados em k consultas, onde k é o número de tipos específicos).

É realmente vale o benchmarking para ver qual deles é mais rápido na realidade. A segunda parece melhor porque é (provavelmente) menos consultas, mas cada uma dessas consultas tem de executar uma junção com a tabela intermediária do m2m. No primeiro caso, você faz uma consulta de junção, e depois muitos mais simples. Alguns backends de banco de dados um melhor desempenho com lotes de pequenas consultas que menos queridos e mais complexos.

Se a segunda é realmente significativamente mais rápido para o seu caso de uso, e você está disposto a fazer algum trabalho extra para limpar o seu código, deve ser possível escrever um método gerente personalizado para o modelo genérico que "-fetches pré "todos os dados subtipo das tabelas específicas relevantes para um determinado queryset, usando apenas uma consulta por tabela subtipo; semelhante à forma como esse trecho otimiza chaves estrangeiras genéricos com uma pré-busca a granel. Isto lhe daria as mesmas consultas como sua segunda opção, com a sintaxe Secador de sua primeira opção.

Outras dicas

Não é uma resposta completa, mas você pode evitar um grande número de visitas ao fazer isso

items= list(items)
for item in items:
    spec = getattr(item, item.get_my_specific_type())

em vez disso:

for item in items:
    spec = getattr(item, item.get_my_specific_type())

Na verdade, forçando um elenco para uma lista de python, você forçar o ORM Django para carregar todos os elementos em seu queryset. Em seguida, ele faz isso em uma consulta.

eu acidentalmente stubmled sobre o seguinte post, o que praticamente responde a sua pergunta:

http://lazypython.blogspot.com/2008 /11/timeline-view-in-django.html

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top