Question

J'ai la structure de modèle suivante:

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):
    ...

Dites, j'ai besoin de récupérer tous les modèles de type Spécifiques , ayant une relation avec un conteneur particulier.

Le SQL pour cela est plus ou moins trivial, mais ce n’est pas la question. Malheureusement, je n'ai pas beaucoup d'expérience avec les ORM (les ORM de Django en particulier), il se peut donc que je manque un modèle ici.

Lorsque cela est fait de manière brute,

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())

cela se traduit par une tonne de résultats positifs (un pour chaque enregistrement générique, qui se rapporte à un conteneur), ce n’est donc évidemment pas la façon de le faire. Maintenant, cela pourrait peut-être être fait en récupérant directement les objets SpecificX:

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

Ainsi, la base de données sera touchée une fois pour chaque type spécifique (acceptable, je suppose).

Je sais que .select_related () ne fonctionne pas avec les relations m2m, donc ce n'est pas très utile ici.

Pour réitérer, le résultat final doit être une collection d'objets SpecificX (non génériques).

Était-ce utile?

La solution

Je pense que vous avez déjà exposé les deux possibilités faciles. Soit vous effectuez une seule requête de filtre sur générique, puis vous convertissez chaque élément en son sous-type spécifique (il en résulte n + 1 requêtes, où n est le nombre d'éléments renvoyés), ou vous effectuez une requête distincte pour chaque table spécifique (résultats en k requêtes, où k est le nombre de types spécifiques).

En réalité, il est utile de procéder à une analyse comparative pour déterminer laquelle est la plus rapide dans la réalité. La seconde semble meilleure parce qu'il y a (probablement) moins de requêtes, mais chacune de ces requêtes doit effectuer une jointure avec la table intermédiaire m2m. Dans le premier cas, vous ne faites qu'une requête de jointure, puis de nombreuses requêtes simples. Certaines bases de données fonctionnent mieux avec beaucoup de petites requêtes que moins nombreuses et plus complexes.

Si le second est beaucoup plus rapide pour votre cas d'utilisation et que vous souhaitez effectuer un travail supplémentaire pour nettoyer votre code, il devrait être possible d'écrire une méthode de gestion personnalisée pour le modèle générique qui " va chercher " toutes les données de sous-type des tables spécifiques pertinentes pour un ensemble de requêtes donné, en utilisant une seule requête par table de sous-type; similaire à la façon dont cet extrait optimise les clés étrangères génériques avec une prélecture en bloc. Cela vous donnerait les mêmes requêtes que votre deuxième option, avec la syntaxe DRYer de votre première option.

Autres conseils

Ce n'est pas une réponse complète mais vous pouvez éviter un grand nombre de résultats en procédant ainsi

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

au lieu de ceci:

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

En effet, en forçant une conversion dans une liste python, vous obligez l’organisation django à charger tous les éléments de votre ensemble de requêtes. Il le fait ensuite dans une requête.

J'ai accidentellement bousculé l'article suivant, ce qui a bien répondu à votre question:

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top