Model Design for Orders in Django

A simple model design for Order and order items in Django


Billing or orders management is a typical use case in most applications, there are many Saas solutions for this but still there can be situations where you need an in-house solution, and it can be tricky and time-consuming to make a design thats efficient and needs least queries. This is a basic model design you may add or modify this as per requirement.

The design includes 4 models

  • Items : Stores the items that can be order. Not required but provides consistency.
  • Order: Stores order information and functions ( Total, Subtotal, etc).
  • Order Items: Stores order items information.

import datetime

class Items(models.Model):
  name = models.CharField(max_length=200, null=False)
  price = models.DecimalField(max_digits=6, decimal_places=2, default=0.00)
  active = models.BooleanField(default=True)

  # Records
  created = models.DateTimeField(auto_now_add=True)
  time = models.DateTimeField(auto_now=True)

def increment_order_id():
  """ Creates Order ID of format  ABCD  . Change as per neeed."""
  prefix = 'ABCD'  
  # if you are gonna change it but its not 4 character long 
  # make sure to change the numbers below too
  last_order = Order.objects.all().order_by('id').last()
  if not last_order:
    return str(prefix) + str( + str( + str( + '0000'
  order_id = last_order.order_id
  order_id_int = int(order_id[12:16])
  new_order_id_int = order_id_int + 1
  new_order_id =  str(prefix) + str(str( + str( + str( + str(new_order_id_int).zfill(4)
  return new_order_id

class Order(models.Model):
    """ Order Model """

  PAID = 1

    (NOTPAID, 'Not Paid'),
    (PARTPAID, 'Partial Paid'),
    (PAID, 'Paid'),

  order_id = models.CharField(max_length=20, default=increment_order_id, null=True, blank=True, editable=False)
  payment_status = models.IntegerField(default=NOTPAID, choices=PAYMENT_STATUS, null=False)
  lock = models.BooleanField(default=False, null=False)
  discount = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
  created = models.DateTimeField(auto_now_add=True)
  time = models.DateTimeField(auto_now=True)

  class Meta:
    ordering = ('-created', '-id')

  def __str__(self):
    return '%s' % (self.order_id)

  def subtotal(self):
    total = Decimal('0.00').quantize(Decimal('0.01'))
    for item in self.orderitems.all().filter(status=True):
      total = total + Decimal(item.subtotal()).quantize(Decimal('0.01'))
    return str(total)

  def total(self):
    total = Decimal('0.00').quantize(Decimal('0.01'))
    for item in self.orderitems.all().filter(status=True):
      total = total + Decimal('0.01'))

    return str(total -

class OrderItems(models.Model):
  order = models.ForeignKey(Order, on_delete=models.PROTECT,
                            related_name='orderitems', null=True)
  desc = models.CharField(max_length=500, null=True)

  # Item
  item = models.ForeignKey(Items, on_delete=models.PROTECT, null=True)
  price = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
  quantity = models.IntegerField(default=1, null=False)
  tax = models.DecimalField(max_digits=10, decimal_places=4, default=0.00)
  discount = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)

  # Records
  created = models.DateTimeField(auto_now_add=True)
  time = models.DateTimeField(auto_now=True)

  def __str__(self):
    return '%s' % (

  def description(self):
    # If items is linked, return item's description
    if self.item:
      return str(
      return 'Order Item'

  def subtotal(self):
    total = ((self.price * Decimal(self.quantity)).quantize(
        Decimal('0.01')) -
    return str(total)

  def taxtotal(self):
    subtotal = Decimal(self.subtotal()).quantize(Decimal('0.01'))
    total = subtotal *
    return str(Decimal(total).quantize(Decimal('0.01')))

  def total(self):
    total = Decimal(self.subtotal()).quantize(Decimal('0.01')) + Decimal(
    return str(total)


Wow ! you have someting to tell us. That's great! Please keep in mind that comments are moderated, we employ rel="nofollow" for links, avoid using a spammy word or a domain in name field, it might end up as a Spam. Thanks for reading.

Last 5 Articles

All Articles >

  1. Security Myths

  2. Model Design for Orders in Django

  3. Automating Business Operations

  4. Unit Testing in Python using assertAlmostEqual

  5. 3 Facebook Tips and Tricks Your Local Business Will Benefit


News Letter

Subscribe to our email newsletter for useful tips and valuable resources, sent out every new article release.