9

I have this model:

class Invoice(models.Model):
    owner  = models.ForeignKey(settings.AUTH_USER_MODEL)
    data   = models.TextField(default=None, blank=True, null=True)
    number = models.PositiveIntegerField(default=0, null=False)

What I need is to auto-increment the field number for each separated user. The rationale is that each user has a list of Invoice, starting from number=1 to number=latest.number+1.

I do known about F() expressions, but can't figure out how to reference the latest/greatest number for each specific user. Maybe Invoice.objects.filter(owner=request.user).aggregate(Max('number')) is the path, but how do I ensure there is no race conditions between Max() and F()?

caruccio
  • 295
  • 3
  • 11
  • Which database are you using? – Alasdair Nov 25 '15 at 17:13
  • @Alasdair Postgres 9.4 – caruccio Nov 25 '15 at 17:20
  • 1
    @MTaqi an auto increment field will increment regardless of which user the Invoice belongs to. I need to be able to have Invoice.number=1 for all users. Thinks of your own telco invoice numbering: it starts from 1 and increments until you call to cancel the service, for each client. – caruccio Nov 25 '15 at 17:22
  • It'd be nice to have something like `models.PositiveIntegerField(auto_increment_for('owner'))` – caruccio Nov 25 '15 at 17:27
  • Why not have a date field, which will tell you the latest invoice as well for each user. –  Nov 26 '15 at 04:17
  • @LegoStormtroopr Because I need invoices to be numbered as a sequence. That seams to be the most natural way. – caruccio Nov 26 '15 at 11:35

3 Answers3

0

You can achieve this and similar functions by overriding save method in model and writing your custom logics to it.

class Invoice(models.Model):
    owner  = models.ForeignKey(settings.AUTH_USER_MODEL)
    data   = models.TextField(default=None, blank=True, null=True)
    number = models.PositiveIntegerField(default=0, null=False)

    def save(self, *args, **kwargs):
        if self.pk:
            self.number += 1
        # Write all your logic here, like handeling max value etc
        return super(Invoice, self).save(*args, **kwargs)
varnothing
  • 1,269
  • 1
  • 17
  • 30
  • 1
    Ok, but that won't prevent a race condition, when 2 concurrent clients try to save at (almost) the same time. That is why I believe it must be done using F() expression. That way the increment operation is done at the database level. – caruccio Nov 26 '15 at 12:32
  • you can handle IntegrityError and wait for random time to avoid race condition. ref: http://stackoverflow.com/a/3523439/1240938 – varnothing Nov 26 '15 at 16:51
  • May be F() is better option. I haven't used it ever. – varnothing Nov 26 '15 at 16:57
  • Here is the best solution to your problem: check latest saved invoice: http://stackoverflow.com/a/28199417/1240938 – varnothing Nov 26 '15 at 17:00
  • This is not a solution for multithreading applications, neither are any of both options in the comments. – Igor Rodriguez Aug 02 '19 at 08:49
0

you can get your first or last object like this:

# For last Object
Model.objects.latest('field') # Field can be id or pk or ...

# For first Object
Model.objects.all().first() # You can also use it on filter
Mojtaba Arezoomand
  • 2,140
  • 8
  • 23
0

A simple solution is you can make the number field as the primary key since its nature would be similar.

class Invoice(models.Model):
    owner  = models.ForeignKey(settings.AUTH_USER_MODEL)
    data   = models.TextField(default=None, blank=True, null=True)
    number = models.IntegerField(primary_key=True)

Or, you can make number as AutoField or BigAutoField.

number = models.AutoField()