How to tell if a model instance is new or not when using UUIDField as a Primary Key

django model choice field
django primary key
django list field
django models
django onetoonefield
django manytomanyfield
django charfield max length
django related_name

I have a model that requires some post-processing (I generate an MD5 of the body field).

models.py

class MyModel(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    body = models.TextField()
    md5 = models.CharField(max_length=32)
    ...

    def save(self, *args, **kwargs):
        if self.pk is None: # Only for create (edit disabled)
            self.md5 = get_md5(self.body)
            super(MyModel, self).save(*args, **kwargs)

The problem is that the final block won't execute because I don't see a way to check if the instance is new or not: self.pk is never None because a UUID is populated before saving.

I'd like to know what the best practice is for handling this.

Thanks in advance.

Update:

The only solution I can think of is to call the database directly and:

  1. Check if the id exists
  2. Compare the modified and created fields to tell if it's an edit
EDIT

self.pk is never None because a UUID is populated before saving.

Instead of setting a default for id, use a method to set id for the new instance.

class MyModel(...):
    id = models.UUIDField(primary_key=True, default=None,...)

    def set_pk(self):
        self.pk = uuid.uuid4()

    def save(self, *args, **kwargs):
        if self.pk is None:
            self.set_pk()
            self.md5 = get_md5(self.body)
            super(MyModel, self).save(*args, **kwargs)

Model field reference | Django documentation, See also. If the built-in fields don't do the trick, you can try django-localflavor for “no data;” the Django convention is to use the empty string, not NULL . If choices are given, they're enforced by model validation and the default form When the field is a primary key, the default is also used when the field is set to None . If I have a model that has a UUID primary key and the the user may set the value on creation, is there any way to tell within the save method that the instance is new? Previous techniques of checking the auto assigned fields: In a django model custom save() method, how should you identify a new object? do not work.

I just got the same issue in my project and found out that you can check the internal state of the model's instance:

def save(self, *args, **kwargs):
    if self._state.adding: # Only for create (edit disabled)
        self.md5 = get_md5(self.body)
        super(MyModel, self).save(*args, **kwargs)

But this solution relies on internal implementation and may stop working after Django is updated

#24377 (UUIDField as primary key breaks inline admins) – Django, TabularInline): model = Child class ParentAdmin(admin. Please let me know if I should not set the Severity in the future. Status: new → assigned Since the use of UUIDField as a primary key requires default=uuid.uuid4 , initializing this issue, so the issue is certainly pertaining validation of the inline form or instance. @Kipei: the main issues is the I-F you have such a natural value - then yes, you can use it as a primary key.BUT: values like DATETIME for instance are NOT useful for a clustering key, since they have a 3.33ms accuracy only, and thus duplicates can exist.

As I've answered here as well, the cleanest solution I've found that doesn't require any additional datetime fields or similar tinkering is to plug into the Django's post_save signal. Add this to your models.py:

from django.db.models.signals import post_save
from django.dispatch import receiver

@receiver(post_save, sender=MyModel)
def mymodel_saved(sender, instance, created, **kwargs):
    if created:
        # do extra work on your instance
        self.md5 = get_md5(self.body)

This callback will block the save method, so you can do things like trigger notifications or update the model further before your response is sent back over the wire, whether you're using forms or the Django REST framework for AJAX calls. Of course, use responsibly and offload heavy tasks to a job queue instead of keeping your users waiting :)

Add PrimaryKeyAsUUIDModel abstract model · Issue #268 , A reasonably common pattern in people using Django is to check if instance.pk to determine whether or not they're acting on a persisted model  The convention for creating the primary key is case insensitive, i.e., if you will change the case of the properties to lowercase (e.g., studentid), uppercase (STUDENTID) or mixedcase (e.g., STuDentiD), then it will still treat that property as the primary key but the name of the column in database will be in property case.

Looks like the cleanest approach to this is to make sure that all your models have a created date on them by inheriting from an Abstract model, then you simply check if created has a value:

models.py

class BaseModel(models.Model):
    """
    Base model which all other models can inherit from.
    """
    id = fields.CustomUUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    class Meta:
        # Abstract models are not created in the DB
        abstract = True

class MyModel(BaseModel):
    my_field = models.CharField(max_length=50)

    def save(self, *args, **kwargs):
        if self.created:
            # Do stuff
            pass
        super(MyModel, self).save(*args, **kwargs)

UUIDField - Django Models, The database will not generate the UUID for you, so it is recommended to use default . primary_key, If True, this field is the primary key for the model. You can specify such a composite primary key with a separate composite primary key class (see "Using Annotations") A composite primary key class has the following characteristics: It is a POJO class. It must be public and must have a public no-argument constructor. If you use property-based access, the properties of the primary key class must be public or protected. It must be serializable. It must define equals and hashCode methods.

Historical Model Customizations, By default, the historical table of a model will use an AutoField for the table's history_id (the history table's primary key). The example below uses a UUIDField instead of an AutoField : For instance, if your application name is polls and your model name Question , then the table name will be polls_historicalquestion . In the extremely rare case you wish to declare a model with no primary key, you can specify primary_key = False in the model Meta options. Non-integer primary keys ¶ If you would like use a non-integer primary key (which I generally don’t recommend), you can specify primary_key=True when creating a field.

Models and Fields, Because we have not specified a primary key, peewee will automatically add an For instance, if you have a virtual table with an un-typed column but you know In this example we will create a UUID field for postgresql (which has a native to create a new instance for a model using a non-autoincrementing primary key,  The default value is used when new model instances are created and a value isn’t provided for the field. When the field is a primary key, the default is also used when the field is set to None.

Automatically Drawing Primary Key Values in Managed BOs, In managed scenarios with UUID keys, the values for primary key fields can be SAP - ABAP RESTful Programming Model For more information, see Numbering. If you do not set the key field to read-only, the key value can also be given by a new travel instance when using managed numbering for the UUID key field  primary_key: If True, sets the current field as the primary key for the model (A primary key is a special database column designated to uniquely identify all the different table records). If no field is specified as the primary key then Django will automatically add a field for this purpose.

Comments
  • So, self.md5 is None for a new instance, right? Why not use self.md5 in place of self.pk then?
  • I could do that, but there's also a few other things that happen (another model gets created with an FK back to this one). Unfortunately, I neglected to add that to this trivial example :/
  • As possible solutions you can try to use post-save signals. It contains boolean flag - created. But this approach will add additional query to db.
  • I don't think you understood my question, how will this help?
  • I appreciate the help, there's no way I can see (without calling the DB) to tell if the object is new or not.
  • Thanks for this @xyres. It's a solution but I'm putting common functionality like id = models.UUIDField... in a common model that I'm inheriting from on all my models, would I have to then override save on all of them?
  • @DanielvanFlymen Just put the save() and set_key() methods in the parent model. The children models will inherit these methods.