Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django 1.5 + pymysql error: ImportError: cannot import name Thing2Literal

I try to use django1.5 and pymysql as MySQLdb as here How to make Django work with unsupported MySQL drivers such as gevent-mysql or Concurrence's MySQL driver?

In the top of my management command:

+try:
+    import pymysql
+    pymysql.install_as_MySQLdb()
+except ImportError:
+    pass 

but get error:

/local/lib/python2.7/site-packages/django/db/backends/mysql/base.py", line 30, in <module>
    from MySQLdb.converters import conversions, Thing2Literal
ImportError: cannot import name Thing2Literal

how to fix it?

like image 523
Evg Avatar asked Mar 09 '13 18:03

Evg


2 Answers

In MySQLdb, the Thing2Literal method is not used if you're using a recent enough version of MySQL, in which case the connection's string_literal method is used instead when a connection is available.

You'll need to patch pymysql so that it does the same thing and lets you use the connection's method.

Context

This method is used for escaping SQL statements. PLaying around with it consequently has security issues that you must consider.

The reason why you want to use the connection's method is the charset, which plays a role in escaping.

Fix the ImportError

This is a pretty easy one, you just need to implement a dummy Thing2Literal method in pymysql.converters. We'll never call it anway, so we don't care about it:

def _Thing2Literal(o,d):
    """
    Implemented for compatibility with Django.
    This function is overriden by the connection's escape method when one is available.
    """
    raise NotImplementedError('Thing2Literal is only implemented through the Connection object.')

 Thing2Literal = _Thing2Literal

Monkey-patching Thing2Literal at runtime when an connection is available

In pymysql.connections.Connection, add: import pymysql.converters

At the end of pymysql.connections.Connection.__init__, add the following:

pymysql.converters.Thing2Literal = lambda o, d: self.escape(o)

And at the end of pymysql.connections.Connection.__del__, add the reverse:

pymysql.converters.Thing2Literal = pymysql.converters._Thing2Literal

We can discard the d argument because it's a dictionary of existing conversions, which are already available to the Connection.escape method.

Caveats

There's a good chance this will break, and expose security issues.
Additionally, I'm pretty sure it will break badly if you have several active connections that use different charsets.


You might need to play around a bit with Django too to make sure it uses your monkey-patched version when available - namely replace from MySQLdb.converters import Thing2Literal with something that still binds the Thing2Literal name to the module.

You can of course achieve the same effect without patching django and making the _Thing2Literal function smarter.

like image 29
Thomas Orozco Avatar answered Sep 30 '22 16:09

Thomas Orozco


Just ran into same issue on Django 1.5.1 and PyMySQL 0.5

Fixed by using CMGS fork (https://github.com/CMGS/PyMySQL) of PyMySQL. Hopefully this will make it's way into mainline PyMySQL. See CMGS's pull request here: https://github.com/petehunt/PyMySQL/pull/106

Judging from author's comments and the feedback in the pull request, I think it's pretty solid for production use.

Example requirements.txt line: -e git://github.com/CMGS/PyMySQL.git#egg=PyMySQL-dev

like image 182
Ken Colton Avatar answered Sep 30 '22 17:09

Ken Colton