2017-04-23 62 views
7

我正在使用postgres/SQLAlchemy/Flask-Admin处理Flask应用程序。但是,在Admin界面中,由于unicode(exc)会产生UnicodeDecodeError,因此无法报告包含Unicode字母的任何数据库错误。SQLAlchemy在异常中的Unicode问题

我能够一起找到问题sqlalchemy.exc

class StatementError(SQLAlchemyError): 
    ... 
    def __unicode__(self): 
     return self.__str__() 

并重现该问题:

class A(Base): 
    __tablename__="a" 
    id = Column(Integer, primary_key=True) 
    name = Column(String) 
    name2 = Column(String, nullable=False) 

session = Session() 
a = A(name=u"עברית") 
session.add(a) 

try: 
    session.commit() 
except Exception as e: 
    print(repr(e)) 
    print("------------------") 
    print(unicode(e)) 

将返回:

ProgrammingError('(psycopg2.ProgrammingError) column "name" of relation "a" does not exist\nLINE 1: INSERT INTO a (name, name2) VALUES (\'\xd7\xa2\xd7\x91\xd7\xa8\xd7\x99\xd7\xaa\', NULL) RETURNING...\n      ^\n',) 
------------------ 
Traceback (most recent call last): 
    File "test.py", line 27, in <module> 
    print(unicode(e)) 
UnicodeDecodeError: 'ascii' codec can't decode byte 0xd7 in position 118: ordinal not in range(128) 

我目前解决这个问题用我的从解码的类取代相关的例外。但是,这是一个可怕的黑客,我正在寻找一个适当的解决方案:

  • 有没有办法配置SQLAlchemy自动解码接收到的错误消息?
  • 有没有办法在latin编码(不太有利,但accetable)
  • 有没有办法让unicode尝试utf-8,而不是ascii/latin解码配置Postgres的输出信息?
  • 有什么办法可以解决它?

(问题是相关的只有Python2在Python3上述工程的代码。我相信这是因为默认的编码是utf-8

+0

我所说的,在SQLAlchemy的TBH一个bug,'__str__'应该返回一个'str'和'__unicode__'应该返回一个'unicode'。您应该将此作为问题提交。你可以通过在所有地方执行print(str(e).decode(“utf-8”))来解决它,这很乏味,但不是世界上最糟糕的事情。 – univerio

+0

我会将此作为一个问题。但是我无法按照建议修复它,因为相关代码位于Flask-Admin中。我需要一个更全球化的解决方案。 – tmrlvi

+0

@tmrlvi您可以在这里发帖链接到您提交的问题吗? –

回答

2

其实,我觉得从您的应用程序打补丁的SQLAlchemy是正确合理的清洁解。原因如下:

  • 您已经确定了一些通常被认为是SQLAlchemy中的错误的东西。

  • 您可以编写一个修补程序,该修补程序在SQLAlchemy当前使用的所有情况下表现相同。也就是说,你的补丁不会破坏现有的代码

  • 即使SQLAlchemy修复了,修补程序也是无害的,这个概率是非常高的。

  • 进行此更改可降低整个代码中SQLAlchemy错误对解决方案的影响,例如更改可能打印异常的每个位置。

  • 更改PostGres返回latin1编码实际上没有帮助,因为python使用ascii编码,当给定latin1字符串时会给出相同的错误。此外,更改PostGres以返回latin1错误可能会涉及更改连接编码;这可能会导致unicode数据的问题。

下面是一个简单的程序,用于修补sqlalchemy.exc.StatementError并测试修补程序。如果你想要,你甚至可以尝试生成一个包括unicode的异常,将其转换为unicode,并且只有在引发UnicodeDecodeError时才应用该补丁。如果你这样做了,当sqlalchemy修复问题时,你的补丁会自动停止应用。

# -*- coding: utf-8 -*- 
from sqlalchemy.exc import StatementError 

def statement_error_unicode(self): 
    return unicode(str(self), 'utf-8') 
# See <link to sqlalchemy issue>; can be removed once we require a 
# version of sqlalchemy with a fix to that issue 
StatementError.__unicode__ = statement_error_unicode 

message = u'Sqlalchemy unicode ' 
message_str = message.encode('utf-8') 
error = StatementError(message_str, 'select * from users', tuple(), '') 
print unicode(error)