J'ai une structure de base de données qui peut être simplifiée comme suit (version et AdditionalInfo non présentées depuis ne sont pas directement liés à ma question):Django: récupérer seul objet de la relation ManyToMany efficace
class Image(models.Model):
file = models.ImageField(blank=False, null=False, upload_to='images')
version = models.ForeignKey(Version, blank=False, null=False)
class ExampleImage(models.Model):
example = models.ForeignKey('Example', blank=False, null=False)
image = models.ForeignKey(Image, blank=False, null=False)
additional_info = models.ForeignKey(AdditionalInfo, blank=True, null=True)
class Example(models.Model):
name = models.Charfield(max_length=255)
images = models.ManyToManyField(Image, through=ExampleImage, through_fields=('example', 'image')
def get_default_image(self):
try:
image = self.images.get(version=Version.objects.get(name=DEFAULT_VERSION))
except Image.DoesNotExist:
# Get some other image, this happens maybe 1 out of 10
Maintenant, j'ai un très besoin commun d'interroger tous les objets Example avec leur image par défaut. À l'heure actuelle, je fais un peu comme suit:
example_list = []
examples = Example.objects.all()
for example in examples:
example_dict = dict(name=example.name, image=example.get_default_image())
example_list.append(example_dict)
# Then show example_list in template
Cette approche fonctionne mais il provoque des milliers de requêtes de base de données et prend plus d'une minute pour effectuer ce qui est peu trop de temps pour télécharger une page web! Donc, ma question est, quelle est la bonne approche pour optimiser ce type de cas d'utilisation. Je pourrais définir le champ default_image pour le modèle Example (et utiliser select_related alors, mais cela rend l'ajout d'images plus compliqué), coder en dur la méthode get_default_image (pas si flexible), faire la méthode get_default_image en cached_property etc. mais je suis pas exactement sûr quelle approche devrait être utilisée. J'ai déjà essayé de nombreux trucs mais rien ne semble aider ma situation. Je devrais trouver une solution qui me permet d'interroger à la fois l'exemple et l'image par défaut avec une grande requête, plutôt que de faire le travail dans la boucle.
Oui, DEFAULT_VERSION est une constante de module. D'après ce que je comprends, Django devrait utiliser la base de données uniquement pour le premier exemple et utiliser le résultat mis en cache pour le reste des exemples? Je vais vérifier Prefetch personnalisé plus tard aujourd'hui, merci pour votre aide! – m5seppal
Django est intelligent, mais pas * ça * intelligent. Vous appelez 'get_default_image' sur chaque modèle, et cette méthode récupère un objet de la base de données chaque fois qu'elle est exécutée. Il y a une mise en cache du jeu de queues, mais seulement pour le même ensemble de queueset ** ** d'un QS, par ex. Après avoir parcouru 'examples ', appeler' examples [0] 'ne retournera plus dans la base de données tant que vous serez dans la même portée. 'Model.objects.get()' essayera toujours de récupérer un objet de la base de données. – Geotob
Votre méthode fonctionne en effet, je suis maintenant capable de le faire en 4 requêtes! Mais il y a quelques problèmes. Premièrement, 'image' est une liste, j'ai donc besoin d'y accéder 'image.0.file.url' dans un template. Un autre problème est que l'image par défaut n'existe pas pour chaque objet, dans ce cas, une image de remplacement doit être récupérée (voir mon implémentation get_default_image). Mon approche actuelle ne le fait pas. – m5seppal