Django - AutoField в отношении внешнего ключа - PullRequest
1 голос
/ 22 октября 2010

У меня есть модель с уникальным целым числом, которое необходимо увеличивать в отношении внешнего ключа, и следующий код - это то, как я в настоящее время обрабатываю ее:

class MyModel(models.Model):
    business = models.ForeignKey(Business)
    number = models.PositiveIntegerField()
    spam = models.CharField(max_length=255)

    class Meta:
        unique_together = (('number', 'business'),)

    def save(self, *args, **kwargs):
        if self.pk is None:  # New instance's only
            try:
                highest_number = MyModel.objects.filter(business=self.business).order_by('-number').all()[0].number
                self.number = highest_number + 1
            except ObjectDoesNotExist:  # First MyModel instance
                self.number = 1
        super(MyModel, self).save(*args, **kwargs)

У меня есть следующие вопросы по этому поводу:

  1. Несколько человек могут создать MyModel экземпляров для одного и того же business по всему Интернету.Возможно ли, чтобы 2 человека создавали MyModel экземпляров одновременно, и .count() возвращает 500 одновременно для обоих, а затем оба пытаются по существу установить self.number = 501 одновременно (вызывая IntegrityError)?Ответ кажется очевидным «да, это может произойти», но я должен был спросить.
  2. Существует ли ярлык или «Лучший способ» для этого, который я могу использовать (или, возможно, SuperAutoField который справляется с этим)?

Я не могу просто шлепнуть while model_not_saved: try:, except IntegrityError:, потому что другие ограничения в модели могут привести к бесконечному циклу и катастрофехуже, чем Чернобыль (может быть, не так уж и плохо).

1 Ответ

2 голосов
/ 22 октября 2010

Вы хотите это ограничение на уровне базы данных. В противном случае вы столкнетесь с проблемой параллелизма, которую вы обсуждали. Решение состоит в том, чтобы обернуть всю транзакцию (чтение, увеличение, запись) в транзакцию.

Почему вы не можете использовать AutoField вместо PositiveIntegerField?

number = models.AutoField()

Однако в этом случае число почти наверняка будет равно yourmodel.id, так почему бы просто не использовать это?

Edit:

О, я вижу, что вы хотите. Вам нужно числовое поле, которое не увеличивается, если не существует более одного экземпляра MyModel.business.

Я бы по-прежнему рекомендовал просто использовать поле id, если вы можете, так как оно обязательно будет уникальным. Если вы абсолютно не хотите этого делать (возможно, вы показываете этот номер пользователям), то вам нужно будет обернуть свой метод сохранения в транзакцию.

Подробнее о транзакциях вы можете прочитать в документации:

http://docs.djangoproject.com/en/dev/topics/db/transactions/

Если вы просто используете это, чтобы подсчитать, сколько экземпляров MyModel имеют FK to Business, вы должны сделать это как запрос, а не пытаться сохранить счет.

...