#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.

Reply via email to