Django Custom User UUID Duplicated (cached?)

500 views Asked by At

I'm Using Django with a custom user model with custom UUID and custom user manager:

class CustomUser(AbstractUser):
    id = models.UUIDField(primary_key=True, default=generate_id(), editable=False)
# ...

As you can see, I'm NOT using default=uuid4(). Instead I've made a function my on my own that uses uuid() to generate the custom id also using the timestamp:

from datetime import datetime as dt
from uuid import uuid1, uuid3, uuid4

def generate_id():
    timestamp = str(dt.timestamp(dt.now()))
    return uuid3(uuid4(),timestamp)

Now if I open the interactive shell: python manage.py shell and I call my function several time, this is what I get:

>>> generate_id()
UUID('bd8279f1-9b4b-3d7d-8932-f9e725f17045')
>>> generate_id()
UUID('0c2ec2ad-b062-3915-a9e9-22842c6f5ea2')
>>> generate_id()
UUID('2289202b-f252-3b27-bcae-cd44825bf4e0')
>>> generate_id()
UUID('88676ea9-4902-36ac-857d-929cb133089c')
>>> generate_id()
UUID('4a18b33e-12f0-3803-8ff0-0c4f0d1c849c')

but when I try creating 2 users:

from users.models import CustomUser
>>> one = CustomUser.objects.create_user(email='[email protected]',password='JJlsaks16112')
>>> two = CustomUser.objects.create_user(email='[email protected]',password='JJlsaks16112')


Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "/usr/local/lib/python3.8/site-packages/django/db/backends/mysql/base.py", line 73, in execute
    return self.cursor.execute(query, args)
  File "/usr/local/lib/python3.8/site-packages/MySQLdb/cursors.py", line 206, in execute
    res = self._query(query)
  File "/usr/local/lib/python3.8/site-packages/MySQLdb/cursors.py", line 319, in _query
    db.query(q)
  File "/usr/local/lib/python3.8/site-packages/MySQLdb/connections.py", line 259, in query
    _mysql.connection.query(self, query)
MySQLdb._exceptions.IntegrityError: (1062, "Duplicate entry 'b448f583acfb31b7955926689b60f28a' for key 'PRIMARY'")

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/foodbook24-api/users/models.py", line 23, in create_user
    user.save()
  File "/usr/local/lib/python3.8/site-packages/django/contrib/auth/base_user.py", line 67, in save
    super().save(*args, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/django/db/models/base.py", line 753, in save
    self.save_base(using=using, force_insert=force_insert,
  File "/usr/local/lib/python3.8/site-packages/django/db/models/base.py", line 790, in save_base
    updated = self._save_table(
  File "/usr/local/lib/python3.8/site-packages/django/db/models/base.py", line 895, in _save_table
    results = self._do_insert(cls._base_manager, using, fields, returning_fields, raw)
  File "/usr/local/lib/python3.8/site-packages/django/db/models/base.py", line 933, in _do_insert
    return manager._insert(
  File "/usr/local/lib/python3.8/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/django/db/models/query.py", line 1254, in _insert
    return query.get_compiler(using=using).execute_sql(returning_fields)
  File "/usr/local/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1397, in execute_sql
    cursor.execute(sql, params)
  File "/usr/local/lib/python3.8/site-packages/django/db/backends/utils.py", line 98, in execute
    return super().execute(sql, params)
  File "/usr/local/lib/python3.8/site-packages/django/db/backends/utils.py", line 66, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "/usr/local/lib/python3.8/site-packages/django/db/backends/utils.py", line 75, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/usr/local/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "/usr/local/lib/python3.8/site-packages/django/db/utils.py", line 90, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/usr/local/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "/usr/local/lib/python3.8/site-packages/django/db/backends/mysql/base.py", line 73, in execute
    return self.cursor.execute(query, args)
  File "/usr/local/lib/python3.8/site-packages/MySQLdb/cursors.py", line 206, in execute
    res = self._query(query)
  File "/usr/local/lib/python3.8/site-packages/MySQLdb/cursors.py", line 319, in _query
    db.query(q)
  File "/usr/local/lib/python3.8/site-packages/MySQLdb/connections.py", line 259, in query
    _mysql.connection.query(self, query)
django.db.utils.IntegrityError: (1062, "Duplicate entry 'b448f583acfb31b7955926689b60f28a' for key 'PRIMARY'")

If I go into the database and modify the id of my first custom user, I'm able to create a second user but as you can see from the screenshot below, the ID does not change!

enter image description here

I guess is a caching issue. Anyone as any idea of how can I solve it? (possibly keeping my custom UUID function) Thanks.

1

There are 1 answers

0
willeM_ Van Onsem On BEST ANSWER

You are calling the function, and therefore the default=… parameter [Django-doc] will be set to the result of the response, not the function, and thus indeed, each time you generate an object, it will use that result.

You thus should pass a reference to the callable, not the result:

class CustomUser(AbstractUser):
    #                                          no parenthesis ↓
    id = models.UUIDField(primary_key=True, default=generate_id, editable=False)