Изменить текстовую фабрику в Django / sqlite - PullRequest
6 голосов
/ 30 апреля 2010

У меня есть проект django, в котором используется база данных sqlite, в которую может записываться внешний инструмент. Предполагается, что текст будет UTF-8, но в некоторых случаях будут ошибки в кодировке. Текст взят из внешнего источника, поэтому я не могу контролировать кодировку. Да, я знаю, что мог бы написать «слой обертывания» между внешним источником и базой данных, но я предпочитаю не делать этого, тем более что база данных уже содержит много «плохих» данных.

Решение в sqlite состоит в том, чтобы изменить text_factory на что-то вроде: lambda x: unicode(x, "utf-8", "ignore")

Однако я не знаю, как сказать это драйверу модели Django.

Исключение, которое я получаю:

'Could not decode to UTF-8 column 'Text' with text' in /var/lib/python-support/python2.5/django/db/backends/sqlite3/base.py in execute

Каким-то образом мне нужно сказать драйверу sqlite не пытаться декодировать текст как UTF-8 (по крайней мере, не используя стандартный алгоритм, но он должен использовать мой отказоустойчивый вариант).

Ответы [ 5 ]

9 голосов
/ 19 июня 2010

Решение в sqlite - изменить text_factory к чему-то вроде: лямбда х: юникод (х, "utf-8", "Игнорировать")

Однако я не знаю, как сказать это драйверу модели Django.

Вы пробовали

from django.db import connection
connection.connection.text_factory = lambda x: unicode(x, "utf-8", "ignore")

перед выполнением каких-либо запросов?

2 голосов
/ 01 марта 2015

Вдохновленный ответом Миллы, рассмотрим следующий патч обезьяны, который устанавливает более толерантную текстовую базу в соединение django sqlite Используется, когда вы не можете контролировать, как текст добавляется в базу данных sqlite, а его может не быть в utf-8. Конечно, используемая здесь кодировка может быть неправильной, но по крайней мере ваше приложение не будет аварийно завершено.

import types
from django.db.backends.sqlite3.base import DatabaseWrapper

def to_unicode( s ):
    ''' Try a number of encodings in an attempt to convert the text to unicode. '''
    if isinstance( s, unicode ):
        return s
    if not isinstance( s, str ):
        return unicode(s)

    # Put the encodings you expect here in sequence.
    # Right-to-left charsets are not included in the following list.
    # Not all of these may be necessary - don't know.
    encodings = (
        'utf-8',
        'iso-8859-1', 'iso-8859-2', 'iso-8859-3',
        'iso-8859-4', 'iso-8859-5',
        'iso-8859-7', 'iso-8859-8', 'iso-8859-9',
        'iso-8859-10', 'iso-8859-11',
        'iso-8859-13', 'iso-8859-14', 'iso-8859-15',
        'windows-1250', 'windows-1251', 'windows-1252',
        'windows-1253', 'windows-1254', 'windows-1255',
        'windows-1257', 'windows-1258',
        'utf-8',     # Include utf8 again for the final exception.
    )
    for encoding in encodings:
        try:
            return unicode( s, encoding )
        except UnicodeDecodeError as e:
            pass
    raise e

if not hasattr(DatabaseWrapper, 'get_new_connection_is_patched'):
    _get_new_connection = DatabaseWrapper.get_new_connection
    def _get_new_connection_tolerant(self, conn_params):
        conn = _get_new_connection( self, conn_params )
        conn.text_factory = to_unicode
        return conn

    DatabaseWrapper.get_new_connection = types.MethodType( _get_new_connection_tolerant, None, DatabaseWrapper )
    DatabaseWrapper.get_new_connection_is_patched = True
0 голосов
/ 22 сентября 2014
from django.db import connection
connection.cursor()
connection.connection.text_factory = lambda x: unicode(x, "utf-8", "ignore")

В моем конкретном случае мне нужно было установить connection.connection.text_factory = str

0 голосов
/ 14 ноября 2013

Кажется, что эта проблема возникает довольно часто и представляет большой интерес для многих людей. (Поскольку этот вопрос имеет более тысячи просмотров и довольно много голосов)

Итак, вот ответ, который я нашел для проблемы, которая представляется мне наиболее удобной:

Я проверил разъем django sqlite3 и добавил преобразование str непосредственно в функцию get_new_connection(...):

def get_new_connection(self, conn_params):
    conn = Database.connect(**conn_params)
    conn.create_function("django_date_extract", 2, _sqlite_date_extract)
    conn.create_function("django_date_trunc", 2, _sqlite_date_trunc)
    conn.create_function("django_datetime_extract", 3, _sqlite_datetime_extract)
    conn.create_function("django_datetime_trunc", 3, _sqlite_datetime_trunc)
    conn.create_function("regexp", 2, _sqlite_regexp)
    conn.create_function("django_format_dtdelta", 5, _sqlite_format_dtdelta)
    conn.text_factory = str
    return conn

Кажется, все работает как надо, и нет необходимости проверять проблему с юникодом в каждом отдельном запросе. Не следует ли считать это добавлением этого к коду django (?), Поскольку я бы не советовал кому-либо на самом деле модифицировать его бэкэнд-код django ...

0 голосов
/ 30 апреля 2010

Подайте данные с помощью одной из волшебных функций str от Django:

smart_str(s, encoding='utf-8', strings_only=False, errors='strict')

или

smart_unicode(s, encoding='utf-8', strings_only=False, errors='strict')
...