使用SQLAlchemy无法从开发服务器连接到Google Cloud SQL

3

我试图使用SQLAlchemy 0.7.9从我的开发工作站连接到Google Cloud SQL,但一直失败(希望使用create_all()生成模式)。我无法解决以下错误:

sqlalchemy.exc.DBAPIError: (AssertionError) No api proxy found for service "rdbms" None None

我已经成功地使用google_sql.py instancename连接到数据库实例,它最初打开了浏览器以授权连接(现在似乎已经缓存了授权,尽管我没有像https://developers.google.com/cloud-sql/docs/commandline所示的那样有~/Library/Preferences/com.google.cloud.plist文件)。
以下是用于测试连接的简单应用程序:
from sqlalchemy import create_engine

engine = create_engine('mysql+gaerdbms:///myapp', connect_args={"instance":"test"})
connection = engine.connect()

完整的堆栈跟踪在此处可用 - https://gist.github.com/4486641
2个回答

4
在SQLAlchemy中,mysql+gaerdbms:///驱动程序只能使用rdbms_apiproxy DBAPI,而当从Google App Engine实例访问Google Cloud SQL时才能使用。我向SQLAlchemy提交了一个工单,要求更新驱动程序,以便在不在Google App Engine上时使用基于OAuth的rdbms_googleapi,就像App Engine SDK提供的Django驱动程序一样。rdbms_googleapi也是google_sql.py(远程SQL控制台)使用的DBAPI。
预计更新后的方言将成为0.7.10和0.8.0版本的一部分,但在它们可用之前,您可以执行以下操作:

1-将工单中的更新方言复制到文件中(例如gaerdbms_dialect.py)

from sqlalchemy.dialects.mysql.mysqldb import MySQLDialect_mysqldb
from sqlalchemy.pool import NullPool
import re

"""Support for Google Cloud SQL on Google App Engine

Connecting
-----------

Connect string format::

    mysql+gaerdbms:///<dbname>?instance=<project:instance>


  # Example:
  create_engine('mysql+gaerdbms:///mydb?instance=myproject:instance1')
"""


class MySQLDialect_gaerdbms(MySQLDialect_mysqldb):

    @classmethod
    def dbapi(cls):
        from google.appengine.api import apiproxy_stub_map

        if apiproxy_stub_map.apiproxy.GetStub('rdbms'):
            from google.storage.speckle.python.api import rdbms_apiproxy
            return rdbms_apiproxy
        else:
            from google.storage.speckle.python.api import rdbms_googleapi
            return rdbms_googleapi

    @classmethod
    def get_pool_class(cls, url):
        # Cloud SQL connections die at any moment
        return NullPool

    def create_connect_args(self, url):
        opts = url.translate_connect_args()
        opts['dsn'] = ''  # unused but required to pass to rdbms.connect()
        opts['instance'] = url.query['instance']
        return [], opts

    def _extract_error_code(self, exception):
        match = re.compile(r"^(\d+):").match(str(exception))
        code = match.group(1)
        if code:
            return int(code)

dialect = MySQLDialect_gaerdbms

2 - 注册自定义方言(可以覆盖现有模式)

from sqlalchemy.dialects import registry
registry.register("mysql.gaerdbms", "application.database.gaerdbms_dialect", "MySQLDialect_gaerdbms")
注意:0.8版本允许在当前进程中注册方言(如上所示)。如果您正在运行旧版本的SQLAlchemy,建议升级到0.8+,否则您需要根据此处的说明为方言创建单独的安装程序。

3 - 更新create_engine('...') URL,因为项目和实例现在作为URL的查询字符串的一部分提供。

mysql+gaerdbms:///<dbname>?instance=<project:instance>

例如:

举个例子:

create_engine('mysql+gaerdbms:///mydb?instance=myproject:instance1')

你是否将其与最新的SDK一起使用?我尝试了SQLAlchemy 0.8.0附带的方言以及您上面发布的方言,但仍然遇到与此处描述的相同错误:http://stackoverflow.com/questions/16192979/using-sqlalchemy-with-app-engine-and-google-cloud-sql-on-development-server - moraes
我找到了一个解决方法,并更新了你的方言:http://stackoverflow.com/a/16198395/125967 - moraes
1
抱歉,SO不能及时通知评论,我刚看到这条消息。在使用本地mysql实例进行开发时,我使用不同的连接字符串/驱动程序(默认的mysql://(mysql-python)驱动程序),并且只有在我的Flask / Flask-Script项目中使用Production或ProductionDeploy配置时才使用mysql+gaerdbms。我通常也不使用dev_appserver,而是使用标准的WSGI服务器。看起来这是一个值得推广至SQLAlchemy的好改变。 - Sean Lynch
我开了一个工单来提出改进方言的建议。我误解了,以为你是在与开发服务器一起使用它。现在,它也可以这样使用。感谢你的帮助。 :) - moraes

3
我认为我有一个可行的示例脚本,如下所示。您可以尝试类似的操作,然后告诉我它的效果如何?
但是(您可能已经意识到了),如果您的目标是在Google Cloud SQL实例上创建模式,那么您可以使用本地mysql服务器创建模式,使用mysqldump将其导出,然后将模式导入Google Cloud SQL。在开发中使用本地mysql服务器非常方便。
from sqlalchemy import create_engine
from google.storage.speckle.python.api import rdbms as dbi_driver
from google.storage.speckle.python.api import rdbms_googleapi

INSTANCE = 'YOURPROJECT:YOURINSTANCE'
DATABASE = 'YOURDATABASE'

def get_connection():       
    return rdbms_googleapi.connect('', instance=INSTANCE, database=DATABASE)

def main():
    engine = create_engine(
        'mysql:///YOURPROJECT:YOURINSTANCE/YOURDATABASE',
        module=dbi_driver,
        creator=get_connection,
    )
    print engine.execute('SELECT 1').scalar()

if __name__ == '__main__':
    main()

这个可行,我能够运行 SELECT 1。它也像 google_sql.py 一样通过了 OAuth 流程。 - Sean Lynch
昨晚我仔细研究了现有的mysql+gaerdbms:///方言(http://goo.gl/WxpCV),它使用`from google.appengine.api import rdbms,而这个又使用了google.storage.speckle.python.api.rdbms_apiproxy,而不像Django驱动程序那样聪明地在非生产/GAE实例上使用google.storage.speckle.python.api.rdbms_googleapi`(http://goo.gl/4ScgJ)。 - Sean Lynch
我正在尝试找到一种方法,可以更新mysql+gaerdbms:///方言以根据环境切换使用哪个API,就像Django驱动程序那样(if apiproxy_stub_map.apiproxy.GetStub('rdbms'): "use rdbms_apiproxy" else "use rdbms_googleapi")。 - Sean Lynch
哦,而且我无法从我的映射中生成模式,即使使用您上面的方法。我得到:sqlalchemy.exc.DatabaseError: (DatabaseError) 1146: Table 'test.users' doesn't exist 'DESCRIBE users' () - Sean Lynch
谢谢您的帮助。您的示例启发了我如何使用rdbms_googleapi(我最初尝试创建自己的OAuth流程,但这是错误的)。 - Sean Lynch
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接