2010-04-30 5 views
6

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

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

Однако, я не знаю, как сказать водителю модельную Джанго это.

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

'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 (по крайней мере, не используя стандартный алгоритм, но для этого нужно для использования моего отказобезопасного варианта).

ответ

9

Раствор в SQLite является изменение text_factory на что-то вроде: лямбда х : unicode (x, "utf-8", "ignore")

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

Вы пробовали

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

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

+0

Спасибо за вход! Вышеописанная работа с несколькими модификациями (а именно, сначала нужно создать курсор, иначе DatabaseWrapper.connection - None). Я разрывал волосы по этому поводу. – Krumelur

+0

@ Krumelur может опубликовать полное решение? – user985541

0

поток данных с одним из волшебной str function из Джанго:

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

или

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

Прошу прощения, если я вас неправильно понимаю, но проблема в том, что база данных уже содержит «плохие» данные, и я хочу сделать преобразование, когда прочитаю его. Страница, на которую вы ссылаетесь, похоже, связана с вводом строк в базу данных. Инструмент, который импортирует данные, не использует django, но работает с модулем pysqlite. Он состоит из устаревшего кода, который я неохотно меняю. Спасибо за ответ. – Krumelur

+0

Вы пытались заполнить «плохой» контент БД двумя вышеуказанными функциями? – maersu

+0

smart_str и smart_unicode могут использоваться для фильтрации того, загружаете ли вы данные в базу данных или читаете из нее. Я бы сделал оба для согласованности и целостности данных. –

0

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

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

Я проверил Джанго sqlite3 разъем и добавил преобразование ул непосредственно к функции 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 

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

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

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

2

Вдохновленный ответом Миллы, рассмотрите следующий патч обезьяны, который устанавливает более толерантный text_factory в соединение 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

Одна деталь не в порядке. Вы должны сделать это исправление, обращаясь к базе данных. Хорошее место может быть в «models.py». – EMS