Am working on an invoice app building, which has a requirement that invoice numbers should be unique & alphanumeric with sequence should be generated in the table.
I Am using PostgreSQL. eg: INV-0001 , INV-0002 etc…
In the older way without ORM or other tech-stack, I used to create a table sequence number and trigger in PostgreSQL, something similar, which will be handled at the database level, which is high concurrent no use will get repeated number and good performance.
CREATE SEQUENCE invoice_number_seq;
CREATE TABLE invoices (
id SERIAL PRIMARY KEY,
invoice_number VARCHAR(20) UNIQUE
);
CREATE OR REPLACE FUNCTION generate_invoice_number()
RETURNS TRIGGER AS $$
BEGIN
NEW.invoice_number := 'INV-' || LPAD(nextval('invoice_number_seq')::TEXT, 3, '0');
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER set_invoice_number
BEFORE INSERT ON invoices
FOR EACH ROW
EXECUTE FUNCTION generate_invoice_number();
But in Django way ORM I believe we can not create a trigger but we can implement this in a different way
To avoid race conditions and add atomic transaction
from django.db import models, transaction
class ControlSequence(models.Model):
name = models.CharField(max_length=100, unique=True)
sequence_number = models.IntegerField(default=0)
prefix = models.CharField(max_length=10)
@classmethod
def get_next_number(cls, sequence_name):
with transaction.atomic():
sequence = cls.objects.select_for_update().get(name=sequence_name)
sequence.sequence_number += 1
sequence.save()
return sequence.sequence_number
This will be called in all other model managers as below before saving
# Get the next invoice number
next_invoice_number = ControlSequence.get_next_number('invoice')
print(f'Next invoice number: {next_invoice_number}')
using above way , I do not need to create a trigger in db,
I can stick to Django and also I can control prefixes based on client requirements, which is common in most ERP systems like JDE, SAP, etc…
My question is
- SInce am new to Django, I need to know, if is it scalable meaning it can be good enough like a trigger in DB, and make sure no ID repeats & works concurrently.
- And how do we do this for bulk_create or bulk_udpate the same concept? if it is a db trigger it will take care while bulk_save. how do we handle efficiency in bulk_save without performance bottleneck?
- How do we handle in Admin portal