views:

105

answers:

3

I can't see any provision for this in the django docs, so how do people go about doing this.

My specific case is this.

I have a shopping cart, each cart instance has an invoice number field, however the invoice number is only generated if the cart goes to a paid status, so not all shopping cart instances will have an invoice number. I want all invoice numbers to be sequential with no gaps between them, so the default pk isn't perfect in this case, so I want a class variable that acts as a counter for the invoice numbers, and is accessable by all instances.

+1  A: 

The default primary key will already be a unique monotonic integer (even in SQLite if you don't delete any records), so you can just use that for it.

Ignacio Vazquez-Abrams
He said he doesn't want any gap: in case of transaction failed, if he uses the PK, he will probably have one.
piro
A: 

You can create a field in a model and state it as primary key or use your current primary key if you are migrating a legacy database primary_key=True.

Django ORM has no support for complex keys. It doesn't allow you to use a table without primary key.

If you need a specific logic for your invoice number generation (for example # of client + '-' + year number), you can create a field for this key, but this will not be a primary key. Call this key generation function on object save in Model.save function if this key is not specified for current object yet.

Vestel
A: 

Have your invoice field in your model:

class Cart(models.Model):
 .....
 invoice_id = models.PositiveIntegerField(null=True,unique=True)

Only set the value when appropriate.

To find the next ID:

try:
  nextID = Cart.objects.exclude(invoice_id=None).order_by('-invoice_id')[0].invoice_id + 1
except IndexError:
  nextID = 1

Having thought about it some more... it occurs to me that invoices might be better served as a separate model. You can link them to your carts with ForeignKey("invoice",null=True). They can then be created when needed, the default primary key field can be your monotonically increasing number, and can then have a separate life-cycle to your cart objects.

MattH
Looks good. I'd prefer it if grabbing the last invoice ID and saving the next one was an atomic operation, though. That way you'd be absolutely sure that different orders can't step on each others toes. Probably pretty doable with the extra method in Django's ORM.
Stijn Debrouwere