Django 3 Dynamic Default Value in Model

Viewed 666

There are similar questions but none recent and based on Django 3 so here it goes:

I am using py nanoid to generate some unique IDs so in my models I have

from nanoid import generate

and then

class Work(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField()
    published = models.BooleanField(False)
    date = models.DateTimeField()
    nanoid = models.CharField(max_length=15, default = generate(size=10) )
    
    
    def __str__ (self):
        return self.title

my aim is to ensure that a new nanoid is generated each time a new work post is added. At the moment is the same repeated each time I try to add a new Work via admin.

I read some replies about creating a custom class but I am a bit at a loss here!

2 Answers

If you pass a function as a default it must be one that takes no arguments. You can use functools.partial to turn generate into a function that takes no args and has size=10 set as the default

from functools import partial

class Work(models.Model):
   ...
   nanoid = models.CharField(max_length=15, default=partial(generate, size=10))

You can override the save method on your model to do it, also you have to create a helper function to check if the nanoid on your model already exists in your persistent storage.

class Work(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField()
    published = models.BooleanField(False)
    date = models.DateTimeField()
    nanoid = models.CharField(max_length=15)
    
    def save(self, *args, **kwargs):
        nanoid = generate_id()
        super(Work, self).save(*args, **kwargs)

    def __str__ (self):
        return self.title

# Helpers
def generate_id():
    """
    Generate nanoid unique code.
    """
    n_id = generate(size=10)
    while not is_available_code(n_id):
        n_id = generate(size=10)
    return n_id

def is_available_id(id):
    """
    Validate Id.
    """
    return not id_is_nanoid(id)

def id_is_nanoid(id):
    return Work.objects.filter(nanoid=id).exists()
Related