How to automatically set the model field during the creation ?

Simple use case which is for sure used a lot by all developers but I’m not sure how to approach it in Django :frowning: (still learning the framework).
Model has field unique_id which should be unique 4 hexa digits.
Every time the new object is created, the unique_id should be automatically calculated and stored.

How can I do this ?

My idea was to have a function which is “bind” to default then disable this field on user-view so user cannot change it, but it seems my improvisation is not working :frowning:

class Product(models.Model):
    unique_id = models.CharField(max_length=4, default=generate_unique_id(), unique=True)
    # ... other code ...
    def generate_unique_id():
        # generate the code and then check if it's really unique among other model objects
        while True:
            u_id = uuid.uuid4().hex[:4].upper()
            if not Product.objects.filter(unique_id=u_id).exists():
                break
            return u_id

The docs for the default parameter specifies that you want to supply a callable - in this case, the function name generate_unique_id. What you’re actually doing is calling the function, which returns your value and has assigned that one value as the default for all instances.
What you want to do is remove the parens from after the function name.

Ken

Note: 4 hex digits is a fairly small field - 65536 possible values. You may not have that many rows, but you will start seeing duplicate values being generated far earlier than that. (As a very rough estimator, the square root of the range gives you an idea of how soon you would get a duplicate - in this case, 256. So you can expect to start seeing duplicate values being selected about half the time once you’ve got 256 rows.)

Side note, but, is there a reason you aren’t using UUIDField for your unique_id?

@KenWhitesell Thanks for you feedback, now I modified the code but something is still wrong - the column is not even created when running migrations (tried to delete and run migrations from scratch).

I guess this will not work as I want, is the problem that I want to run through all Product.objects in the function ?

def generate_unique_id():
    while True:
        u_id = uuid.uuid4().hex[:4].upper()
        if not Product.objects.filter(unique_id=u_id).exists():
            break
    return u_id
class Product(models.Model):
    unique_id = models.CharField(max_length=4, default=generate_unique_id, unique=True)
    # ... other fields ...

Ok, I gave up - too much effort for such simple problem :slight_smile:

So I decided to use id field as unique product number:

class Product(models.Model):
    # THIS IS NOT WORKING unique_id = models.CharField(max_length=4, default=generate_unique_id, unique=True)
    # WORKAROUND - use ID and add zeros before it when printing...
    def unique_id(self):
        return str(id).zfill(5)

If the column isn’t created, you’ve got a different issue that would need to be addressed that is completely aside from the default parameter. But it sounds like you’ve moved on, so I won’t pursue that any further.