2009-03-25 13 views
7

Considérese el siguiente esqueleto de un models.py para un juego de conquista del espacio:ORM de Django: Objetos de almacenamiento en caché y la manipulación de ForeignKey

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

Tengo muchos modelos de este tipo de datos para un proyecto que estoy trabajando, y yo cambiar el estado del juego basado en interacciones algo complicadas entre varios objetos de datos. Quiero evitar un montón de llamadas innecesarias a la base de datos, por lo que una vez por turno, que hago algo así como

  1. Consulta todas las flotas, los planetas y otros objetos de la base de datos y almacenar en caché como objetos pitón
  2. Proceso de la los objetos del juego, resolviendo el estado del juego
  3. guardarlos de nuevo en la base de datos

Este modelo parece romper totalmente abajo al usar objetos ForeignKey. Por ejemplo, cuando una nueva flota se aparta un planeta, tengo una línea que se ve algo como esto:

fleet.home.ships -= fleet.ships 

Después de ejecutar esta línea, no tengo otro código que altera el número de barcos en cada uno de los planetas, incluyendo el planeta fleet.home. Lamentablemente, los cambios realizados en la línea anterior no se reflejan en el QuerySet de planetas que obtuve antes, de modo que cuando guardo todos los planetas al final del turno, los cambios en los barcos de fleet.home se sobrescriben.

¿Hay alguna forma mejor de manejar esta situación? ¿O es así como son todos los ORM?

Respuesta

20

ORM de Django no implementa un identity map (está en el ticket tracker, pero no está claro si o cuando se llevará a cabo, al menos un núcleo de Django committer tiene expressed opposition to it). Esto significa que si llega al mismo objeto de base de datos a través de dos rutas de consulta diferentes, está trabajando con diferentes objetos de Python en la memoria.

Esto significa que su diseño (cargar todo en la memoria a la vez, modificar muchas cosas y luego guardarlo todo al final) es inviable con el ORM de Django. Primero porque a menudo perderá mucha carga de memoria en copias duplicadas del mismo objeto, y segundo porque "sobrescribe" problemas como el que se está ejecutando.

Ya sea que necesite modificar su diseño para evitar estos problemas (tenga cuidado al trabajar con un solo QuerySet a la vez, guarde todo lo modificado antes de realizar otra consulta, o si carga varias consultas, busque todas las relaciones manualmente , nunca atraviese ForeignKeys utilizando los atributos convenientes para ellos), o utilice un ORM de Python alternativo que implemente el mapa de identidad. SQLAlchemy es una opción.

Tenga en cuenta que esto no significa que el ORM de Django es "malo". Está optimizado para el caso de las aplicaciones web, donde estos tipos de problemas son raros (he hecho desarrollo web con Django durante años y nunca tuve este problema en un proyecto real). Si su caso de uso es diferente, es posible que desee elegir un ORM diferente.

+1

Bien resumen –

+0

Sí, muchas gracias por esta respuesta informativa.Sé que eso no significa que el ORM de Django sea malo; de hecho, he desarrollado un proyecto completo usando Django que no requiere este tipo de procesamiento de datos complicado, y este problema nunca ha surgido, y es por eso que estaba tan perdido. –

Cuestiones relacionadas