Потокобезопасный подсчет значений поля - PullRequest
2 голосов
/ 10 декабря 2011

Есть ли способ сделать что-то подобное в django?

INSERT INTO t VALUES(1,(SELECT x.c2 FROM t x ORDER BY c2 DESC LIMIT 1)+1,1);

У меня есть модель со многими полями.И одно из значений поля должно быть установлено в соответствии с предыдущей записью.

В настоящее время я делаю это простым выбором предыдущей записи.Но это ужасно и не безопасно для потоков.

def save(self, *args, **kw):
    if self.sn is None or self.sn == ns.DEFAULT_GIFT_SN:
        q = Gift.objects.filter(company = self.company).only('id').order_by('-id')
        if q.count():
            last = q[0]
            next_id = int(last.sn) + 1
        else:
            next_id = 1
        self.sn = next_id
    super(Gift, self).save(*args, **kw)

Хочу что-н.Ленивый, как:

def save(self, *args, **kw):
    if self.sn is None or self.sn == ns.DEFAULT_GIFT_SN:
        self.sn = _F('SELECT x.c2 FROM t x ORDER BY c2 DESC LIMIT 1')
    super(Gift, self).save(*args, **kw)

Есть предложения?

ОБНОВЛЕНИЕ (для Сезара):

class Gift(models.Model):
    company = models.ForeignKey(Company)
    certificate = ChainedForeignKey(Certificate,
        chained_field = "company",
        chained_model_field = "company",
        show_all = False,
        auto_choose = True
    )
    gifter = models.ForeignKey(User, null = True, blank = True)
    giftant_first_name = models.CharField(_('first name'), max_length = 30, blank = True)
    giftant_last_name = models.CharField(_('last name'), max_length = 30, blank = True)
    giftant_email = models.EmailField(_('e-mail address'), blank = True)
    giftant_mobile = models.CharField(max_length = 20, blank = True, null = True)
    due_date = models.DateTimeField()
    exp_date = models.DateTimeField()
    sn = models.CharField(max_length = 32, default = ns.DEFAULT_GIFT_SN, editable = False)
    pin_salt = models.CharField(max_length = 5, editable = False, default = gen_salt)
    pin = models.CharField(max_length = 32, null = True, blank = True)
    postcard = models.ForeignKey(Postcard)
    state_status = models.IntegerField(choices = ns.STATE_STATUSES, default = ns.READY_STATE_STATUS)
    delivery_status = models.IntegerField(choices = ns.DELIVERY_STATUSES, default = ns.WAITING_DELIVERY_STATUS)
    payment_status = models.IntegerField(choices = ns.PAYMENT_STATUSES, default = ns.NOT_PAID_PAYMENT_STATUS)
    usage_status = models.IntegerField(choices = ns.USAGE_STATUSES, default = ns.NOT_USED_USAGE_STATUS)
    nominal = models.FloatField(default = 0)
    used_money = models.FloatField(default = 0)
        use_date = models.DateTimeField(blank = True, null = True)
    pub_date = models.DateTimeField(auto_now_add = True)
    cashier = ChainedForeignKey(CompanyUserProfile, blank = True, null = True,
        chained_field = "company",
        chained_model_field = "company",
        show_all = False,
        auto_choose = True
    )

    class Meta:
        unique_together = ('company', 'sn',)

Ответы [ 2 ]

1 голос
/ 11 декабря 2011

Я знаю, while это зло, но вы можете попробовать что-то вроде этого:

sn_is_ok = False
while not sn_is_ok:
    last_id = MyModel.objects.latest('id').id
    try:
        self.sn = last_id + 1
        self.save()
    Except IntegrityError:
        continue
    else:
        sn_is_ok = True

Я не думаю, что вы получите больше, чем 2 петли.

0 голосов
/ 11 декабря 2011

Полагаю, вы можете сделать три вещи, от простого до безумного крайности:

  1. Пусть Gift.sn будет AutoField, если вы можете уйти без необходимости увеличивать значенияотдельно для каждой компании.
  2. Используйте ограничение unique_together = ('company', 'sn') и обновите экземпляр подарка в случае сбоя ограничения уникальности, используйте предоставленный пример @Stan с наихудшим сценарием, который, если у вас много одновременных записейтолько один будет проходить за каждый ход.
  3. Напишите некоторый пользовательский SQL, чтобы получить блокировку таблицы модели Gift, если база данных ее поддерживает.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...