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)# My best guess is this locks the entire table? Without this it doesn't work.
list(Account.objects.select_for_update().all())
# Update ourself
Account.objects.filter(
pk = self.pk,
).annotate(
other_accounts_exist = models.Exists(
Account.objects.filter(
jurisdiction = models.OuterRef("jurisdiction"),
account_number__isnull = False,
).exclude(
pk = models.OuterRef("pk"),
),
),
).update(
account_number = models.Case(
models.When(
other_accounts_exist = True,
then = models.Subquery(
Account.objects.filter(
jurisdiction = models.OuterRef("jurisdiction"),
account_number__isnull = False,
).exclude(
pk = models.OuterRef("pk"),
).order_by(
"-account_number",
).values(
"account_number",
)[:1]
) + models.Value(1),
),
default = 1,
)
)
# Since we did an update we need to refresh ourselves from the db.
self.refresh_from_db()