Django ORM: кеширование и манипулирование объектами ForeignKey - PullRequest
7 голосов
/ 25 марта 2009

Рассмотрим следующий скелет файла models.py для игры о завоевании космоса:

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

У меня много таких моделей данных для проекта, над которым я работаю, и я меняю состояние игры, основываясь на несколько сложных взаимодействиях между различными объектами данных. Я хочу избежать множества ненужных обращений к базе данных, поэтому один раз за ход я делаю что-то вроде

  1. Запрос всех флотов, планет и других объектов из базы данных и кэширование их как объектов Python
  2. Обработка игровых объектов, определение состояния игры
  3. Сохраните их обратно в базу данных

Эта модель полностью разрушается при использовании объектов ForeignKey. Например, когда новый флот покидает планету, у меня есть линия, которая выглядит примерно так:

fleet.home.ships -= fleet.ships

После запуска этой линии у меня есть другой код, который изменяет количество кораблей на каждой из планет, включая планету fleet.home. К сожалению, изменения, сделанные в приведенной выше строке, не отражены в наборе запросов, который я получил ранее, поэтому, когда я сохраняю все планеты в конце хода, изменения для судов fleet.home перезаписываются.

Есть ли лучший способ справиться с этой ситуацией? Или это как раз все ОРМ?

Ответы [ 2 ]

20 голосов
/ 25 марта 2009

ORM в Django не реализует карту идентификации (она есть в системе отслеживания билетов , но неясно, будет ли она реализована или когда будет реализована; по крайней мере, один основной коммиттер Django имеет выраженное возражение против этого ). Это означает, что если вы получаете один и тот же объект базы данных по двум разным путям запросов, вы работаете с разными объектами Python в памяти.

Это означает, что ваш дизайн (загрузите все в память сразу, измените многие вещи, а затем сохраните все обратно в конце) не работает с использованием Django ORM. Во-первых, потому что это часто тратит много времени на загрузку памяти в дублирующих копиях одного и того же объекта, а во-вторых, из-за проблем с «перезаписью», подобных той, с которой вы столкнулись.

Вам либо нужно переделать свой дизайн, чтобы избежать этих проблем (либо будьте осторожны, работая только с одним QuerySet за раз, сохраняя все, что было изменено перед выполнением другого запроса; или, если вы загружаете несколько запросов, посмотрите все отношения вручную, никогда не пересекайте ForeignKeys, используя для них удобные атрибуты), или используйте альтернативный Python ORM, который реализует карту идентичности. SQLAlchemy является одним из вариантов.

Обратите внимание, что это не означает, что ORM Джанго "плохой". Он оптимизирован для случая веб-приложений, где такого рода проблемы встречаются редко (я занимался веб-разработкой с Django в течение многих лет и никогда не сталкивался с такой проблемой в реальном проекте). Если ваш вариант использования отличается, вы можете выбрать другой ORM.

1 голос
/ 22 мая 2009
...