Question

Considérez le squelette suivant d'un models.py pour un jeu de conquête spatiale:

class Fleet(models.Model):
    game = models.ForeignKey(Game, related_name='planet_set')
    owner = models.ForeignKey(User, related_name='planet_set', null=True, blank=True)
    home = models.ForeignKey(Planet, related_name='departing_fleet_set')
    dest = models.ForeignKey(Planet, related_name='arriving_fleet_set')
    ships = models.IntegerField()

class Planet(models.Model):
    game = models.ForeignKey(Game, related_name='planet_set')
    owner = models.ForeignKey(User, related_name='planet_set', null=True, blank=True)
    name = models.CharField(max_length=250)
    ships = models.IntegerField()

J'ai beaucoup de ces modèles de données pour un projet que je travaille, et je change l'état du jeu basé sur les interactions complexes entre les différents un peu d'objets de données. Je veux éviter beaucoup d'appels inutiles à la base de données, une fois par tour, je fais quelque chose comme

  1. Rechercher toutes les flottes, les planètes et autres objets de la base de données et les mettre en cache sous forme d'objets python
  2. Traiter les objets de jeu, la résolution de l'état du jeu
  3. Enregistrer-les dans la base de données

Ce modèle semble totalement casser vers le bas lors de l'utilisation des objets ForeignKey. Par exemple, lorsqu'une nouvelle flotte quitte une planète, j'ai une ligne qui ressemble à quelque chose comme ceci:

fleet.home.ships -= fleet.ships

Après cette ligne fonctionne, je autre code qui modifie le nombre de navires à chacune des planètes, y compris la planète fleet.home. Malheureusement, les modifications apportées à la ligne ci-dessus ne sont pas reflétées dans le QuerySet des planètes que j'ai obtenu plus tôt, de sorte que lorsque j'enregistre toutes les planètes à la fin du tour, les modifications apportées aux navires de fleet.home seront écrasés.

Y at-il une meilleure façon de gérer cette situation? Ou est-ce juste la façon dont tous les ORM sont?

Était-ce utile?

La solution

ORM de Django ne met pas en œuvre une carte d'identité href="http://en.wikipedia.org/wiki/Identity_map" (il est dans le Tracker billet , mais on ne sait pas si ou quand il sera mis en œuvre, au moins un noyau Django committers a a exprimé son opposition à elle ). Cela signifie que si vous arrivez à la même base de données objet par deux chemins différents de la requête, vous travaillez avec différents objets Python en mémoire.

Cela signifie que votre conception (charge tout en mémoire à la fois, de modifier beaucoup de choses, puis enregistrez tout retour à la fin) est inapplicable en utilisant Django ORM. D'abord parce qu'il sera souvent perdre beaucoup de chargement de la mémoire en double exemplaire du même objet, et deuxième en raison de « problèmes » comme en écrasant celui que vous utilisez dans.

Vous avez besoin soit de retravailler votre conception pour éviter ces problèmes (soit faire attention à travailler avec un seul QuerySet à la fois, rien sauver modifié avant de faire une autre requête, ou si vous chargez plusieurs requêtes, regardez toutes les relations manuellement, ne ForeignKeys jamais traverse en utilisant les attributs pratiques pour eux), ou utiliser un ORM Python alternatif qui implémente carte d'identité. SQLAlchemy est une option.

Notez que cela ne signifie pas ORM de Django est « mauvais ». Il est optimisé pour le cas d'applications web, où ce genre de problèmes sont rares (je l'ai fait le développement web avec Django pendant des années et n'a jamais eu ce problème sur un vrai projet). Si votre cas d'utilisation est différent, vous pouvez choisir un autre ORM.

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