#35438: Failed to insert an model instance into db that utilizes a UUID as its
primary key and incorporates a GeneratedField.
-----------------------------------------+------------------------
Reporter: 长孙弘奕 | Owner: nobody
Type: Uncategorized | Status: new
Component: Uncategorized | Version: 5.0
Severity: Normal | Keywords:
Triage Stage: Unreviewed | Has patch: 1
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
-----------------------------------------+------------------------
I have defined a model that employs a UUIDField as its primary key,
accompanied by a JSONField and several GeneratedFields, which are intended
for indexing the JSONFields. However, when attempting to load data via
loaddata from a JSON fixture, the process fails during the execution of
the execute_sql method within django/db/models/SQL/compiler.py. The
problematic code snippet is as follows:
{{{
def execute_sql(self, returning_fields=None):
....
if not self.returning_fields:
return []
....
else:
cols = [opts.pk.get_col(opts.db_table)]
rows = [
(
self.connection.ops.last_insert_id(
cursor,
opts.db_table,
opts.pk.column,
),
)
]
converters = self.get_converters(cols)
if converters:
rows = list(self.apply_converters(rows, converters))
return rows
}}}
Here, returning_fields comprises GeneratedFields, thereby preventing the
method from directly returning an empty list at the outset of its logical
execution. Moreover, since the model's primary key is not an
AUTO_INCREMENT column (it's a UUID), last_insert_id() erroneously returns
0. This subsequently triggers an error within self.apply_converters(rows,
converters) as it attempts to treat 0 as a UUID.
To address this issue, the else block logic requires adjustment to
accommodate the presence of GeneratedField. Alternatively, within
django/db/models/base.py's _save_table method, one could exclude
GeneratedField from returning_fields. A modified approach might resemble:
{{{
#Original: returning_fields = meta.db_returning_fields
returning_fields = [ f for f in meta.db_returning_fields if
not f.generated]
results = self._do_insert(
cls._base_manager, using, fields, returning_fields, raw
)
if results:
for value, field in zip(results[0], returning_fields):
setattr(self, field.attname, value)
return updated
}}}
This adjusted strategy has proven effective in my project, successfully
circumventing the aforementioned issues without introducing new problems.
--
Ticket URL: <
https://code.djangoproject.com/ticket/35438>
Django <
https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.