Opened 13 months ago
Closed 13 months ago
#34918 closed Bug (invalid)
Assigning model instance to `_id`/attname field saves correctly, but breaks accessing the field
Reported by: | Ricardo Busquet | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 4.2 |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Model definition:
class Author(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) class Book(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) author = models.ForeignKey( Author, null=True, blank=True, on_delete=models.CASCADE )
In [1]: book = Book.objects.create() In [2]: author = Author.objects.create() In [3]: book.author_id In [4]: book.author In [5]: book.author_id = author In [6]: book.save() In [7]: book.author --------------------------------------------------------------------------- KeyError Traceback (most recent call last) ... ValidationError: ['“Author object (caaf8f9a-f934-44e9-8e6b-00d71d116acf)” is not a valid UUID.']
Similarly, if someone tries to use book.author_id
to fetch an author using Author.objects.get(id=book.author_id)
will also fail with a similar error.
Change History (3)
comment:1 by , 13 months ago
comment:2 by , 13 months ago
Django generally doesn't validate model attribute assignments. For example, if Book has a title CharField
, you can assign it the wrong type, like datetime
and it'll be silently coerced to string. I don't think we're going to add this sort of validation or document it, but I will leave this ticket open for a second opinion in case I'm overlooking something.
Incidentally, similar steps with id
as an AutoField
also save correctly but subsequently fail on access:
Traceback (most recent call last): File "/home/tim/code/django/django/db/models/fields/related_descriptors.py", line 235, in __get__ rel_obj = self.field.get_cached_value(instance) File "/home/tim/code/django/django/db/models/fields/mixins.py", line 15, in get_cached_value return instance._state.fields_cache[cache_name] KeyError: 'reporter' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/home/tim/code/django/django/db/models/fields/__init__.py", line 2116, in get_prep_value return int(value) TypeError: int() argument must be a string, a bytes-like object or a real number, not 'Reporter' The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/home/tim/code/django/tests/many_to_one/tests.py", line 856, in test_cached_relation_invalidated_on_save self.assertEqual(self.a.reporter, self.r2) File "/home/tim/code/django/django/db/models/fields/related_descriptors.py", line 253, in __get__ rel_obj = self.get_object(instance) File "/home/tim/code/django/django/db/models/fields/related_descriptors.py", line 216, in get_object return qs.get(self.field.get_reverse_related_filter(instance)) File "/home/tim/code/django/django/db/models/query.py", line 633, in get clone = self._chain() if self.query.combinator else self.filter(*args, **kwargs) File "/home/tim/code/django/django/db/models/query.py", line 1476, in filter return self._filter_or_exclude(False, args, kwargs) File "/home/tim/code/django/django/db/models/query.py", line 1494, in _filter_or_exclude clone._filter_or_exclude_inplace(negate, args, kwargs) File "/home/tim/code/django/django/db/models/query.py", line 1501, in _filter_or_exclude_inplace self._query.add_q(Q(*args, **kwargs)) File "/home/tim/code/django/django/db/models/sql/query.py", line 1599, in add_q clause, _ = self._add_q(q_object, self.used_aliases) File "/home/tim/code/django/django/db/models/sql/query.py", line 1631, in _add_q child_clause, needed_inner = self.build_filter( File "/home/tim/code/django/django/db/models/sql/query.py", line 1458, in build_filter return self._add_q( File "/home/tim/code/django/django/db/models/sql/query.py", line 1631, in _add_q child_clause, needed_inner = self.build_filter( File "/home/tim/code/django/django/db/models/sql/query.py", line 1545, in build_filter condition = self.build_lookup(lookups, col, value) File "/home/tim/code/django/django/db/models/sql/query.py", line 1375, in build_lookup lookup = lookup_class(lhs, rhs) File "/home/tim/code/django/django/db/models/lookups.py", line 30, in __init__ self.rhs = self.get_prep_lookup() File "/home/tim/code/django/django/db/models/lookups.py", line 362, in get_prep_lookup return super().get_prep_lookup() File "/home/tim/code/django/django/db/models/lookups.py", line 88, in get_prep_lookup return self.lhs.output_field.get_prep_value(self.rhs) File "/home/tim/code/django/django/db/models/fields/__init__.py", line 2118, in get_prep_value raise e.__class__( TypeError: Field 'id' expected a number but got <Reporter: Paul Jones>.
Hi Ricardo Busquet
I checked this, and you are right. But instead of assigning
book.author_id=author
tobook.author=author
it seems to be working fine.Can we update the documentation for the UUID field to aware reader about this situation or code level changes are needed?
Please feel free to assign this to me