Immutable Django model fields

I wanted to make a field on a Django model read-only after it was assigned an initial value. Maybe I was having a slow day, or my Google-fu was especially lacking, but I couldn't find a documented way to do this. There's the editable Field option, but I wanted to ensure I couldn't mistakenly overwrite the initial value anywhere — not just in the admin interface or form processing. It turned out to be pretty easy.

The solution was to override the Model's __setattr__ method and intercept writes for the Field in question. Here's an example Model:

def random_hash(): 
  digest = hashlib.sha512()
  digest.update("%s%s%s" % (settings.SECRET_KEY, time.time(), random.random()))
  l = long(digest.hexdigest(), 16)
  return struct.pack('d', l).encode('base64').replace('\n', '').strip('=')

class Order(models.Model):
  confirmation_id = models.CharField(max_length=128, default=random_hash)

  def __setattr__(self, name, value):
    if name == 'confirmation_id':
        if getattr(self, 'confirmation_id', None):
            return
    super(Order, self).__setattr__(name, value)

  def __unicode__(self):
    return u"Order %s, confirmation_id: %s" % (self.id, self.confirmation_id)

Not elegant, but it works:

>>> o = Order()
>>> o.save()
>>> o
<Order: Order 4, confirmation_id: vzb0jXVz6l8>
>>> o.confirmation_id = 'something else'
>>> o
<Order: Order 4, confirmation_id: vzb0jXVz6l8>
>>>

A more general solution would be a Model subclass with a list of the immutable fields. You can't put that in the model's Meta class (see this ticket), so here's what I use:

from django.db import models

class ImmutableModel(models.Model):
    immutable_fields = []
    class Meta:
        abstract = True

    def __setattr__(self, name, value):
        if name == 'immutable_fields' or name in self.immutable_fields:
            if getattr(self, name, None):
                return
        super(ImmutableModel, self).__setattr__(name, value)

Make your model a subclass of that and set the immutable_fields attribute to a list of fields that should be immutable once set. Good enough to protect you from yourself unless you're really trying.

Comments have been turned off for this article, but you can always contact us about it.