Django с Oracle DB - ORA-19011: Буфер символьной строки слишком мал
У меня есть следующая модель для базы данных Oracle, которая не является частью моего проекта Django:
class ResultsData(models.Model):
RESULT_DATA_ID = models.IntegerField(primary_key=True, db_column="RESULT_DATA_ID")
RESULT_XML = models.TextField(blank=True, null=True, db_column="RESULT_XML")
class Meta:
managed = False
db_table = '"schema_name"."results_data"'
Поле RESULT_XML в самой базе данных объявлено как XMLField. Я решил представить его как TextField в модели Django, из-за отсутствия ограничения на количество символов. Когда я пытаюсь загрузить некоторые данные с помощью этой модели, я получаю следующую ошибку:
DatabaseError: ORA-19011: Character string buffer too small
Я полагаю, что это связано с объемом данных, хранящихся в поле RESULT_XML, поскольку когда я пытаюсь просто вытащить запись с .values("RESULT_DATA_ID"), она вытаскивается нормально.
Есть идеи, как я могу обойти эту проблему? Поиск ответов в гугле пока ничего не дал.
ОБНОВЛЕННЫЙ ОТВЕТ. Я нашел гораздо лучший способ решения этой проблемы - я написал объект преобразования значения пользовательского поля, который генерирует SQL-запрос Oracle, который я искал:
OracleTransforms.py
from django.db.models import TextField
from django.db.models.lookups import Transform
class CLOBVAL(Transform):
'''
Oracle-specific transform for XMLType field, which returns string data exceeding
buffer size (ORA-19011: Character string buffer too small) as a character LOB type.
'''
function = None
lookup_name = 'clobval'
def as_oracle(self, compiler, connection, **extra_context):
return super().as_sql(
compiler, connection,
template='(%(expressions)s).GETCLOBVAL()',
**extra_context
)
# Needed for CLOBVAL to work as a .values('field_name__clobval') lookup in Django ORM queries
TextField.register_lookup(CLOBVAL)
Теперь я могу просто написать запрос следующим образом:
from .OracleTransforms import CLOBVAL
ResultsData.objects.filter(RESULT_DATA_ID=some_id).values('RESULT_DATA_ID', 'RESULT_XML__clobval')
or
ResultsData.objects.filter(RESULT_DATA_ID=some_id).values('RESULT_DATA_ID', XML = CLOBVAL('RESULT_XML'))
Это лучшее решение для меня, поскольку я продолжаю использовать QuerySet, а не RawQuerySet. Единственное ограничение, которое я вижу в этом решении на данный момент, это то, что мне нужно всегда указывать .values(CLOBVAL('RESULT_XML')) в моих ORM запросах, иначе Oracle DB снова сообщит об ORA-19011, но я думаю, что это все еще хороший результат.
СТАРЫЙ ОТВЕТ Итак, я нашел способ обойти проблему, благодаря предложению Кристофера Джонса.
ORA-19011 - это ошибка, на которую Oracle DB отвечает, когда объем данных, которые он отправляет обратно в виде строки, превышает допустимый буфер. Поэтому его нужно отправить обратно как символьный LOB объект.
Django не имеет прямой поддержки этого специфического для Oracle метода (по крайней мере, я ее не нашел), поэтому ответом на проблему был необработанный Django запрос:
query = 'select a.RESULT_DATA_ID, a.RESULT_XML.getClobVal() as RESULT_XML FROM SCHEMA_NAME.RESULTS_DATA a WHERE a.RESULT_DATA_ID=%s'
data = ResultsData.objects.raw(query, [id])
Таким образом, вы получите обратно RawQuerySet, который является менее известным и менее любимым кузеном Django's QuerySet. Вы можете перебирать ответы, и RESULT_XML будет содержать поле LOB, которое при опросе будет преобразовано в тип String.
Обработка XML-данных в кодировке типа String является проблематичной, поэтому я также использовал пакет XMLTODICT Python, чтобы привести их в более цивилизованную форму.
Далее, вероятно, я должен искать способ модифицировать геттер Django только для поля RESULT_XML и заставить его генерировать запрос к Oracle DB с методом .getClobVal() в нем, но я коснусь этого в другом вопросе на StackOverflow: Django - пользовательский геттер для 1 поля в модели