django-pyodbc-azure Ограничение параметров SQL Server

Джанго: 1.11

Я использую django-pyodbc-azure в качестве своего бэкенда Django. Он подключается к SQL Server 2012. SQL Server имеет ограничение в 2100 параметров. Когда я пытаюсь использовать prefetch_related в наборе запросов, который возвращает более 2100 результатов...

def object_search_list(request):
    objects = MyModel.objects.filter(some_field='filter value').select_related('another_field').prefetch_related('a_third_field')
    print(objects)
    return render(request, 'objects/object_list.html', {'objects':objects})

... эта ошибка возвращается:

Exception Value: ('07002', '[07002] [Microsoft][SQL Server Native Client 11.0]COUNT field incorrect or syntax error (0) (SQLExecDirectW)')

Он указывает на серверную часть pyodbc, используемую django-pyodbc-azure. При просмотре функций (https://github.com/michiya/django-pyodbc-azure/blob/azure-2.1/sql_server/pyodbc/features.py), набор max_query_params отсутствует. Я попытался вручную добавить max_query_params = 2100. Это привело к той же ошибке. Установка этого значения на 2000 и 1000 также привела к той же ошибке.

Как настроить django-pyodbc-azure для автоматической обработки этого ограничения SQL Server?

Серверная часть Oracle для Django имеет такую ​​функцию: https://github.com/django/django/blob/master/django/db/backends/oracle/features.py

ОБНОВЛЕНИЕ:

Я понял, что имя max_query_params было новым для Django 2.0. Поэтому я попробовал max_limit_in_size, чтобы он соответствовал Django 1.11. Просто django-pyodbc-azure не поддерживает количество управляющих параметров, а base.py переопределяет стандартное значение Django base.py?

ОБНОВЛЕНИЕ 2:

Я обновился до Django 2.1 и обновил django-pyodbc-azure, чтобы соответствовать. Затем я отредактировал features.py, чтобы добавить max_query_params = 2000 в качестве функции. Другое место, где max_query_params появляется в нативных бэкэндах Django, — это operations.py в этой функции (пример Oracle):

def bulk_batch_size(self, fields, objs):
    """Oracle restricts the number of parameters in a query."""
    if fields:
        return self.connection.features.max_query_params // len(fields)
    return len(objs)

django-pyodbc-azure тоже имеет эту функцию, и выглядит она так:

def bulk_batch_size(self, fields, objs):
    """
    Returns the maximum allowed batch size for the backend. The fields
    are the fields going to be inserted in the batch, the objs contains
    all the objects to be inserted.
    """
    objs_len, fields_len, max_row_values = len(objs), len(fields), 1000
    if (objs_len * fields_len) <= max_row_values:
        size = objs_len
    else:
        size = max_row_values // fields_len
    return size

Кажется, что он уже позаботился о пакетной обработке с max_row_values, установленным на 1000. Однако та же ошибка сохраняется. Я также пытался изменить 1000 на max_query_params безрезультатно.

Вот полная трассировка:

Traceback:

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\db\backends\utils.py" in _execute
  85.                 return self.cursor.execute(sql, params)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\sql_server\pyodbc\base.py" in execute
  546.             return self.cursor.execute(sql, params)

The above exception (('07002', '[07002] [Microsoft][SQL Server Native Client 11.0]COUNT field incorrect or syntax error (0) (SQLExecDirectW)')) was the direct cause of the following exception:

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\core\handlers\exception.py" in inner
  34.             response = get_response(request)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\core\handlers\base.py" in _get_response
  126.                 response = self.process_exception_by_middleware(e, request)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\core\handlers\base.py" in _get_response
  124.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\contrib\auth\decorators.py" in _wrapped_view
  21.                 return view_func(request, *args, **kwargs)

File "C:\Users\user\djangoproject\app\views.py" in object_list
  486.     return render(request, 'objects/object_list.html', {'objects':objects})

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\shortcuts.py" in render
  36.     content = loader.render_to_string(template_name, context, request, using=using)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\template\loader.py" in render_to_string
  62.     return template.render(context, request)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\template\backends\django.py" in render
  61.             return self.template.render(context)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\template_timings_panel\panels\TemplateTimings.py" in timing_hook
  139.         result = func(self, *args, **kwargs)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\template\base.py" in render
  171.                     return self._render(context)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\test\utils.py" in instrumented_test_render
  96.     return self.nodelist.render(context)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\template\base.py" in render
  937.                 bit = node.render_annotated(context)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\template\base.py" in render_annotated
  904.             return self.render(context)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\template\loader_tags.py" in render
  150.             return compiled_parent._render(context)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\test\utils.py" in instrumented_test_render
  96.     return self.nodelist.render(context)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\template\base.py" in render
  937.                 bit = node.render_annotated(context)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\template\base.py" in render_annotated
  904.             return self.render(context)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\template_timings_panel\panels\TemplateTimings.py" in timing_hook
  139.         result = func(self, *args, **kwargs)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\template\loader_tags.py" in render
  62.                 result = block.nodelist.render(context)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\template\base.py" in render
  937.                 bit = node.render_annotated(context)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\template\base.py" in render_annotated
  904.             return self.render(context)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\template\loader_tags.py" in render
  188.             return template.render(context)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\template_timings_panel\panels\TemplateTimings.py" in timing_hook
  139.         result = func(self, *args, **kwargs)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\template\base.py" in render
  173.                 return self._render(context)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\test\utils.py" in instrumented_test_render
  96.     return self.nodelist.render(context)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\template\base.py" in render
  937.                 bit = node.render_annotated(context)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\template\base.py" in render_annotated
  904.             return self.render(context)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\template\defaulttags.py" in render
  166.             len_values = len(values)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\db\models\query.py" in __len__
  250.         self._fetch_all()

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\db\models\query.py" in _fetch_all
  1188.             self._prefetch_related_objects()

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\db\models\query.py" in _prefetch_related_objects
  723.         prefetch_related_objects(self._result_cache, *self._prefetch_related_lookups)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\db\models\query.py" in prefetch_related_objects
  1569.                 obj_list, additional_lookups = prefetch_one_level(obj_list, prefetcher, lookup, level)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\db\models\query.py" in prefetch_one_level
  1699.     all_related_objects = list(rel_qs)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\db\models\query.py" in __iter__
  268.         self._fetch_all()

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\db\models\query.py" in _fetch_all
  1186.             self._result_cache = list(self._iterable_class(self))

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\db\models\query.py" in __iter__
  54.         results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\db\models\sql\compiler.py" in execute_sql
  1065.             cursor.execute(sql, params)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\debug_toolbar\panels\sql\tracking.py" in execute
  186.         return self._record(self.cursor.execute, sql, params)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\debug_toolbar\panels\sql\tracking.py" in _record
  124.             return method(sql, params)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\db\backends\utils.py" in execute
  100.             return super().execute(sql, params)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\db\backends\utils.py" in execute
  68.         return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\db\backends\utils.py" in _execute_with_wrappers
  77.         return executor(sql, params, many, context)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\db\backends\utils.py" in _execute
  85.                 return self.cursor.execute(sql, params)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\db\utils.py" in __exit__
  89.                 raise dj_exc_value.with_traceback(traceback) from exc_value

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\django\db\backends\utils.py" in _execute
  85.                 return self.cursor.execute(sql, params)

File "C:\Users\user\installed\anaconda\envs\django2\lib\site-packages\sql_server\pyodbc\base.py" in execute
  546.             return self.cursor.execute(sql, params)

Exception Type: Error at url
Exception Value: ('07002', '[07002] [Microsoft][SQL Server Native Client 11.0]COUNT field incorrect or syntax error (0) (SQLExecDirectW)')

person OverflowingTheGlass    schedule 19.12.2018    source источник


Ответы (1)


Не похоже, что он был добавлен в django-pyodbc-azure:

https://github.com/michiya/django-pyodbc-azure/blob/azure-2.1/sql_server/pyodbc/features.py#L4

Я добавил его в свою вилку здесь:

https://github.com/FlipperPA/django-pyodbc-azure/blob/azure-2.1/sql_server/pyodbc/features.py#L33

Можете ли вы дать ему тест? Вам нужно будет перейти на Django 2.1, а затем вы сможете pip install git+https://github.com/FlipperPA/[email protected] убедиться, что это так же просто, как добавить этот параметр. При беглом взгляде на исходный код Django кажется, что этого может быть все, что нужно. Если это сработает, я отправлю Михае запрос на включение.

person FlipperPA    schedule 19.12.2018
comment
поэтому я попытался отредактировать файлы django-pyodbc-azure с помощью версии Django 1.11 (max_limit_in_size), и это не сработало с этим небольшим редактированием. Я буду работать над обновлением до 2.1 и установкой вашего форка (или просто отредактирую features.py из репозитория Михайи) - person OverflowingTheGlass; 20.12.2018
comment
Это не работает. Я получаю ту же ошибку после редактирования features.py, чтобы добавить строку max_query_params = 2000 - person OverflowingTheGlass; 02.01.2019
comment
Проверьте мое обновление на мой вопрос - добавлены некоторые детали - person OverflowingTheGlass; 02.01.2019
comment
В этот момент я бы открыл это как проблему на сайте django-pyodbc-azure; поскольку у вас есть работающий тестовый пример, это может помочь Михае. Я предполагаю, что он, возможно, закодировал это, но не имел рабочего примера или тестового примера для тестирования. Удачи! - person FlipperPA; 03.01.2019
comment
Я разместил это как проблему - похоже, что связанная проблема была поднята в прошлом году, и это может быть ограничением prefetch_related Django. Кстати, у вас есть прямой контакт с Михайей? Полностью понимаю, если времени нет, но проблемы накапливаются, и Microsoft PM для драйверов опубликовал вопрос в январе с вопросом о состоянии проекта. Я думаю, что есть много людей, полагающихся на этот проект - любая информация, которую вы могли бы иметь, будет очень признательна! - person OverflowingTheGlass; 26.03.2019
comment
У меня, к сожалению, нет, но у нескольких сотрудников Microsoft есть его контактная информация. Несколько раз я был близок к созданию форка, но в последнее время чаще использую PostgreSQL, чем SQL Server. Посмотрим, появится ли Михая, когда появится версия 2.2. - person FlipperPA; 26.03.2019
comment
Похоже, вы довольно активно участвуете в этом проекте здесь и на GitHub, поэтому я эгоистично хотел бы, чтобы вы сделали форк. Я не думаю, что у меня достаточно опыта, чтобы поднимать тяжести, но я готов помочь, чем смогу. - person OverflowingTheGlass; 27.03.2019
comment
@OverflowingTheGlass Мы запустили ответвление, чтобы перенести это в ядро ​​Django: github.com/ FlipperPA/django-mssql-backend/issues Вы когда-нибудь решали эту проблему? Я хотел бы иметь поддержку max_query_params в выпуске. - person FlipperPA; 11.08.2019
comment
Я этого не сделал - в итоге я переключился на обработку на стороне сервера, так как мой объем в любом случае был бы слишком большим для max_query_params. Отличные новости о вилке! Очень рад, что вы беретесь за это - я посмотрю на проект и посмотрю, могу ли я чем-нибудь помочь. - person OverflowingTheGlass; 12.08.2019