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 поля в модели

Вернуться на верх