Model-level locking#

Model-level locking allows you to prevent saving an object (using the save() method) or deleting it (using the delete() method). If the object you’re trying to save or delete is locked, an ObjectLocked exception is raised which you can use to handle this situation.

Important

When using QuerySet methods such as update or bulk_update, the lock status is not checked and therefore the object may be updated or deleted without throwing the exception.

To use model-level locking, inherit from the abstract LockableModel and implement the is_locked() method, which must return True if your model instance is locked and False otherwise. This function will be evaluated when calling save() or delete().

Optionally, if you want the locked status to be set directly for an instance, implement set_locked(value) and define your own logic for locking or unlocking the object.

Important

In order to determine whether updates are allowed to a locked object, lockable model instances remember whether they have been locked at the moment they are fetched from the database or not, so an instance may be fetched unlocked and then locked manually or automatically.

In that case, the instance will be successfully saved because it has not been locked in the database yet. After that, the instance will be locked and the next successful save must unlock the instance.

Making objects lockable with a flag#

django-object-lock does not provide a default “locked” flag to your model. Instead, you define is_locked() and use your own attribute to indicate that an object is locked or not.

In this case you will need to define set_locked(value) as well to set the “locked” flag to the appropiate value. You need not save the object when implementing set_locked(value).

For example:

from django.db import models
from django_object_lock.models import LockableModel


class Article(LockableModel):
    title = models.CharField(max_length=120)
    is_locked_flag = models.BooleanField(default=False)

    def is_locked(self) -> bool:
        return self.is_locked_flag

    def set_locked(self, value: bool) -> None:
        self.is_locked_flag = value

You could let the user set the is_locked_flag, or add more logic in any of these methods that your may require.

Locking objects automatically according to a condition#

Suppose we wanted to prevent edition of Articles that have been already published. We can implement this logic in our is_locked.

from django.db import models
from django.utils.timezone import now
from django_object_lock.models import LockableModel


class Article(LockableModel):
    title = models.CharField(max_length=120)
    is_locked_flag = models.BooleanField(default=False)
    published_at = models.DateTimeField()

    def is_locked(self) -> bool:
        return self.published_at >= now()

Now published articles may not be edited nor deleted. Again, there is no set_locked(value) method because it would not make sense.