Python 更改Django/sqlite中的文本\u工厂

Python 更改Django/sqlite中的文本\u工厂,python,django,sqlite,pysqlite,Python,Django,Sqlite,Pysqlite,我有一个django项目,它使用一个可以由外部工具写入的sqlite数据库。文本应该是UTF-8,但在某些情况下,编码中会出现错误。文本来自外部源,因此我无法控制编码。是的,我知道我可以在外部源代码和数据库之间编写一个“包装层”,但我宁愿不这样做,尤其是因为数据库已经包含了很多“坏”数据 sqlite中的解决方案是将文本工厂更改为: lambda x:unicode(x,“utf-8”,“忽略”) 然而,我不知道如何告诉Django模型驱动程序这一点 我得到的例外是: '无法解码为UTF-

我有一个django项目,它使用一个可以由外部工具写入的sqlite数据库。文本应该是UTF-8,但在某些情况下,编码中会出现错误。文本来自外部源,因此我无法控制编码。是的,我知道我可以在外部源代码和数据库之间编写一个“包装层”,但我宁愿不这样做,尤其是因为数据库已经包含了很多“坏”数据

sqlite中的解决方案是将文本工厂更改为:

lambda x:unicode(x,“utf-8”,“忽略”)

然而,我不知道如何告诉Django模型驱动程序这一点

我得到的例外是:


'无法解码为UTF-8列'Text'和'Text'
在里面
/execute中的var/lib/pythonsupport/python2.5/django/db/backends/sqlite3/base.py


不知何故,我需要告诉sqlite驱动程序不要尝试将文本解码为UTF-8(至少不使用标准算法,但它需要使用我的故障保护变体)。

使用Django提供的一种神奇方法来输入数据:

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

sqlite中的解决方案是改变 文本将显示为类似以下内容: lambda x:unicode(x,“utf-8”, “忽略”)

然而,我不知道如何告诉Django模型驱动程序这一点

你试过了吗

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

在运行任何查询之前?

看来,这个问题经常出现,而且很多人对此非常感兴趣。(因为这个问题有一千多个观点和相当多的投票)

这是我为这个问题找到的答案,在我看来是最方便的:

我检查了django sqlite3连接器,并将str转换直接添加到
获取新连接(…)
函数:

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
它似乎工作正常,不必在每个请求中单独检查unicode问题。是否应该考虑将其添加到django代码(?),因为我不建议任何人实际手动修改他的django后端代码

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

在我的特定情况下,我需要设置连接。连接。Tr.TrxFiels= STR

< P>受Milla的回答启发,考虑下面的猴子补丁,它将一个更宽容的Tr.x工厂安装到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

不兼容的Django版本。首先检查Django版本以解决此错误。我在Django==3.0.8上运行,它产生了一个错误。然后我运行了virtualenv,其中Django==3.1.2,错误被删除。

如果我误解了您的意思,我很抱歉,但问题是数据库已经包含“坏”数据,我想在读取时进行转换。您所指的页面似乎处理将字符串输入数据库的问题。导入数据的工具不使用django,而是使用pysqlite模块。它包含我不愿意更改的遗留代码。感谢您的回复。您是否尝试将“坏”DB内容填充到上述两个函数中?smart_str和smart_unicode可用于筛选您是将数据加载到数据库还是从中读取数据。我会兼顾一致性和数据完整性。对不起,但我必须承认,你现在把我完全弄糊涂了。我不明白如何在数据库驱动程序级别使用这些函数。无论我如何阅读文档,我只能看到它们对字符串进行操作,但是Sqlite在我获得实际字符串之前抛出了一种异常方式。这个问题被更新了,但我得到了一个例外。我现在意识到我原来的问题没有很清楚地表述出来。问题是我甚至在看到数据之前就出现了异常。只需迭代模型中的记录就足以触发异常。感谢您的输入!上面进行了一些修改(即,必须首先创建一个游标,否则DatabaseWrapper.connection为None)。我一直在为这件事发牢骚。@Krumelur你能发布完整的解决方案吗?遗漏了一个细节。您需要执行此修补程序来访问数据库。“models.py”可能是一个好地方。这个问题是10年前提出的。Django 3.x当时不存在:)
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