mic
mic

Reputation: 1266

Django .values() on field name with '%' sign: "ValueError: unsupported format character '_' (0x5f) at index 83"

I have a model with a field called %_Coverage, and when I try to pass this to .values on a QuerySet, Django throws an error saying "ValueError: unsupported format character '_' (0x5f)". How can I escape the percent sign that it runs correctly?

My_Message = type('My_Message', (models.Model,), {
    '__module__': __name__,
    'id': models.AutoField(primary_key=True),
    'name': 'My_Message',
    '%_Coverage': models.IntegerField
})

class Message:
    My_Message = models.ForeignKey(My_Message, on_delete=models.CASCADE, null=True, blank=True)

messages = Message.objects.filter(message_name='My_Message').values('My_Message__%_Coverage')
print(messages)  # throws an error

# This works:
messages = Message.objects.filter(message_name='My_Message')
print(messages)
# prints: <QuerySet []>

Using f'My_Message__%_Coverage' throws the same error, and 'My_Message__%%_Coverage' complains that the field is not found.

Here is the traceback.

Traceback (most recent call last):
  File "python3.8/site-packages/django/core/handlers/base.py", line 179, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "python3.8/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "python3.8/site-packages/django/views/generic/base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "python3.8/site-packages/rest_framework/views.py", line 505, in dispatch
    response = self.handle_exception(exc)
  File "python3.8/site-packages/rest_framework/views.py", line 465, in handle_exception
    self.raise_uncaught_exception(exc)
  File "python3.8/site-packages/rest_framework/views.py", line 476, in raise_uncaught_exception
    raise exc
  File "python3.8/site-packages/rest_framework/views.py", line 502, in dispatch
    response = handler(request, *args, **kwargs)
  File "python3.8/site-packages/rest_framework/decorators.py", line 50, in handler
    return func(*args, **kwargs)
  File "views/message_viewer.py", line 220, in my_view
    print(messages)
  File "python3.8/site-packages/django/db/models/query.py", line 263, in __repr__
    data = list(self[:REPR_OUTPUT_SIZE + 1])
  File "python3.8/site-packages/django/db/models/query.py", line 269, in __len__
    self._fetch_all()
  File "python3.8/site-packages/django/db/models/query.py", line 1303, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "python3.8/site-packages/django/db/models/query.py", line 111, in __iter__
    for row in compiler.results_iter(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size):
  File "python3.8/site-packages/django/db/models/sql/compiler.py", line 1108, in results_iter
    results = self.execute_sql(MULTI, chunked_fetch=chunked_fetch, chunk_size=chunk_size)
  File "python3.8/site-packages/django/db/models/sql/compiler.py", line 1156, in execute_sql
    cursor.execute(sql, params)
  File "python3.8/site-packages/django/db/backends/utils.py", line 98, in execute
    return super().execute(sql, params)
  File "python3.8/contextlib.py", line 120, in __exit__
    next(self.gen)
  File "python3.8/site-packages/django/db/backends/utils.py", line 113, in debug_sql
    sql = self.db.ops.last_executed_query(self.cursor, sql, params)
  File "python3.8/site-packages/django/db/backends/sqlite3/operations.py", line 160, in last_executed_query
    return sql % params
ValueError: unsupported format character '_' (0x5f) at index 83

Here is the relevant code from Django:

# django/db/backends/sqlite3/operations.py
def last_executed_query(self, cursor, sql, params):
    # Python substitutes parameters in Modules/_sqlite/cursor.c with:
    # pysqlite_statement_bind_parameters(self->statement, parameters, allow_8bit_chars);
    # Unfortunately there is no way to reach self->statement from Python,
    # so we quote and substitute parameters manually.
    if params:
        if isinstance(params, (list, tuple)):
            params = self._quote_params_for_last_executed_query(params)
        else:
            values = tuple(params.values())
            values = self._quote_params_for_last_executed_query(values)
            params = dict(zip(params, values))
        return sql % params
    # For consistency with SQLiteCursorWrapper.execute(), just return sql
    # when there are no parameters. See #13648 and #17158.
    else:
        return sql

Upvotes: 2

Views: 461

Answers (1)

mic
mic

Reputation: 1266

I renamed the field to Percent_Coverage. It seems like a poor idea to have an attribute name with a % sign.

Upvotes: 1

Related Questions