#30114: ValidationError sometimes raised for valid UUIDs with mod_wsgi
-------------------------------------+-------------------------------------
Reporter: Jerry Vinokurov | Owner: nobody
Type: Bug | Status: new
Component: Database layer | Version: 2.1
(models, ORM) |
Severity: Normal | Resolution:
Keywords: validationerror, | Triage Stage:
uuid | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by traeger):
* status: closed => new
* resolution: invalid =>
Comment:
We got a similar problem with the same cause on every call to a view using
a model with an UUIDField which is the foreignkey for an other model.
It seams {{{value}}} in 'get_db_prep_value'
(django/db/models/fields/__init__.py) is an UUID object but not of
instance {{{uuid.UUID}}} as described above.
The stack trace:
{{{
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/db/models/fields/__init__.py" in to_python
2343. return uuid.UUID(**{input_form: value})
File "/usr/lib64/python3.6/uuid.py" in __init__
137. hex = hex.replace('urn:', '').replace('uuid:', '')
During handling of the above exception ('UUID' object has no attribute
'replace'), another exception occurred:
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/core/handlers/exception.py" in inner
34. response = get_response(request)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/core/handlers/base.py" in _get_response
115. response =
self.process_exception_by_middleware(e, request)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/core/handlers/base.py" in _get_response
113. response = wrapped_callback(request,
*callback_args, **callback_kwargs)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/contrib/auth/decorators.py" in _wrapped_view
21. return view_func(request, *args, **kwargs)
[Template Render Call]
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/shortcuts.py" in render
36. content = loader.render_to_string(template_name, context,
request, using=using)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/template/loader.py" in render_to_string
62. return template.render(context, request)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/template/backends/django.py" in render
61. return self.template.render(context)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/template/base.py" in render
171. return self._render(context)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/template/base.py" in _render
163. return self.nodelist.render(context)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/template/base.py" in render
937. bit = node.render_annotated(context)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/template/base.py" in render_annotated
904. return self.render(context)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/template/loader_tags.py" in render
150. return compiled_parent._render(context)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/template/base.py" in _render
163. return self.nodelist.render(context)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/template/base.py" in render
937. bit = node.render_annotated(context)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/template/base.py" in render_annotated
904. return self.render(context)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/template/loader_tags.py" in render
62. result = block.nodelist.render(context)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/template/base.py" in render
937. bit = node.render_annotated(context)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/template/base.py" in render_annotated
904. return self.render(context)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/template/defaulttags.py" in render
166. len_values = len(values)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/db/models/query.py" in __len__
256. self._fetch_all()
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/db/models/query.py" in _fetch_all
1242. self._result_cache =
list(self._iterable_class(self))
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/db/models/query.py" in __iter__
55. results =
compiler.execute_sql(chunked_fetch=self.chunked_fetch,
chunk_size=self.chunk_size)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/db/models/sql/compiler.py" in execute_sql
1127. sql, params = self.as_sql()
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/db/models/sql/compiler.py" in as_sql
489. where, w_params = self.compile(self.where) if
self.where is not None else ("", [])
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/db/models/sql/compiler.py" in compile
405. sql, params = node.as_sql(self, self.connection)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/db/models/sql/where.py" in as_sql
81. sql, params = compiler.compile(child)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/db/models/sql/compiler.py" in compile
405. sql, params = node.as_sql(self, self.connection)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/db/models/fields/related_lookups.py" in as_sql
130. return super().as_sql(compiler, connection)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/db/models/lookups.py" in as_sql
163. rhs_sql, rhs_params = self.process_rhs(compiler,
connection)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/db/models/lookups.py" in process_rhs
260. return super().process_rhs(compiler, connection)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/db/models/lookups.py" in process_rhs
95. return self.get_db_prep_lookup(value, connection)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/db/models/lookups.py" in get_db_prep_lookup
187. [get_db_prep_value(value, connection, prepared=True)]
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/db/models/fields/related.py" in get_db_prep_value
940. return self.target_field.get_db_prep_value(value,
connection, prepared)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/db/models/fields/__init__.py" in get_db_prep_value
2333. value = self.to_python(value)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/db/models/fields/__init__.py" in to_python
2348. params={'value': value},
}}}
We get the same error when query-caching comes into play:
{{{
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/db/models/fields/related_descriptors.py" in __get__
164. rel_obj = self.field.get_cached_value(instance)
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/db/models/fields/mixins.py" in get_cached_value
13. return instance._state.fields_cache[cache_name]
During handling of the above exception ('user'), another exception
occurred:
File "/opt/python/run/venv/local/lib/python3.6/site-
packages/django/db/models/fields/__init__.py" in to_python
2343. return uuid.UUID(**{input_form: value})
File "/usr/lib64/python3.6/uuid.py" in __init__
137. hex = hex.replace('urn:', '').replace('uuid:', '')
During handling of the above exception ('UUID' object has no attribute
'replace'), another exception occurred:
[...]
}}}
May this have to do with the caching-framework somehow?
Maybe the UUID class created by the cache-deserializer differ from
uuid.UUID?
We are using
* django 2.2.12
* python 3.6.10
* wsgi 1.0
* mod_wsgi 3.5
* 15 theads
* running on an EC2 instances with Amazon Linux/2.9.6
* with postgres-SQL 11.5
The problem suddenly occurred after we disabled our AWS-loadbalancing and
moved from multible instances to one single instance with its own ssl-
proxy.
That why I first thought it's an wsgi issue as said above.
But: The problem does not occur with the same wsgi version and
configuration but with a different django and python version:
* django 1.11.29
* python 3.4
* wsgi 1.0
* mod_wsgi 3.5
* 15 theads
* running on an EC2 instances with Amazon Linux/2.9.6
* with postgres-SQL 9.5.10
WSGI Setup:
{{{
[..]
SSLEngine on
SSLCertificateFile [..]
SSLCertificateKeyFile [..]
[..]
WSGIDaemonProcess wsgi-ssl processes=1 threads=15 display-
name=%{GROUP} \
python-home=/opt/python/run/venv \
python-path=/opt/python/current/app \
home=/opt/python/current/app \
user=wsgi \
group=wsgi
[..]
}}}
For now we are using the following bad hotfix which simply enforces a
string conversion before putting the value into the UUID constructor:
{{{
from django.core import exceptions
from django.db import models
import uuid
class FixedUUIDField(models.UUIDField):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def to_python(self, value):
if value is not None and not isinstance(value, uuid.UUID):
'''
original code:
input_form = 'int' if isinstance(value, int) else 'hex'
try:
return uuid.UUID(**{input_form: str(value)})
'''
try:
if isinstance(value, int):
return uuid.UUID(int=value)
else:
return uuid.UUID(hex=str(value))
except (AttributeError, ValueError):
raise exceptions.ValidationError(
self.error_messages['invalid'],
code='invalid',
params={'value': value},
)
return value
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/30114#comment:7>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/069.31b978b0632e4fdc0c0712fe3b789e91%40djangoproject.com.