Saturday, June 1, 2019

Atomic IntegerField increment based on another model instance

Assume the following models for this question:

class Jurisdiction(models.Model):
    name
= models.CharField(max_length = 100)

class Account(models.Model):
    account_number
= models.PositiveIntegerField(blank = True)
    jurisdiction
= models.ForeignKey("Jurisdiction", on_delete = models.CASCADE)

   
class Meta:
       
"""Meta model to define unique_together constraints."""
       
#make the account_number unique for a jurisdiction
        unique_together
= ("account_number", "jurisdiction")

   
def create_account_number(self):
       
"""Create the account number for this account."""
       
# If the account number is already set, do nothing.
       
if self.account_number is not None:
           
return

       
# Get the last account created for the jurisdiction, based on having the largest account number.
        last_account_for_jurisdiction
= Account.objects.filter(jurisdiction = self.jurisdiction).order_by("-account_number").first()
       
# If the account exists set the account number.
       
if last_account_for_jurisdiction is not None:
           
self.account_number = last_account_for_jurisdiction.account_number + 1
       
# Else there are no other accounts for this jurisdiction, set the first account number.
       
else:
           
self.account_number = 1

   
def save(self, *args, **kwargs):
       
# create the account number
       
self.create_account_number()
       
# call the superclass save to write the account to the database.
       
super().save(*args, **kwargs)

Is there a way in Django to be able to atomically set account_number on save based on the largest account number for a given jurisdiction? I was hoping some combination of F expressions, conditional expressions, and subquery expressions would allow me to do this, but they don't appear to allow referencing a different model's fields.

The unique_together will properly prevent duplicate Jurisdiction + account_number pairs from being entered into the DB, but I was hoping there was a way to perform the setting of account_number in an atomic way so I wouldn't have to handle an IntegrityError and try again. I get the feeling what I'm asking for isn't possible, or there's a completely different way I should be going about this. I haven't used AutoField as I need a sequental series of numbers within each Jurisdiction.

Thanks in advance for the help.

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/fc7a0001-a576-46e5-b0a1-aeec500ad77c%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment