I've been unsuccessful at connecting to Google Cloud SQL using SQLAlchemy 0.7.9 from my development workstation (in hopes to generate the schema using create_all()). I can't get passed the following error:
sqlalchemy.exc.DBAPIError: (AssertionError) No api proxy found for service "rdbms" None None
I was able to successfully connect to the database instance using the google_sql.py instancename
, which initially opened up a browser to authorize the connection (which now appears to have cached the authorization, although I don't have the ~/Library/Preferences/com.google.cloud.plist
file as https://developers.google.com/cloud-sql/docs/commandline indicates I should)
Here is the simple application I'm using to test the connection:
from sqlalchemy import create_engine engine = create_engine('mysql+gaerdbms:///myapp', connect_args={"instance":"test"}) connection = engine.connect()
The full stacktrace is available here - https://gist.github.com/4486641
It turns out the mysql+gaerdbms:///
driver in SQLAlchemy was only setup to use the rdbms_apiproxy
DBAPI, which can only be used when accessing Google Cloud SQL from a Google App Engine instance. I submitted a ticket to SQLAlchemy to update the driver to use the OAuth-based rdbms_googleapi
when not on Google App Engine, just like the Django driver provided in the App Engine SDK. rdbms_googleapi is also the DBAPI that google_sql.py
uses (remote sql console).
The updated dialect is expected to be part of 0.7.10 and 0.8.0 releases, but until they are available, you can do the following:
1 - Copy the updated dialect in the ticket to a file (ex. 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:///?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 - Register the custom dialect (you can override the existing schema)
from sqlalchemy.dialects import registry registry.register("mysql.gaerdbms", "application.database.gaerdbms_dialect", "MySQLDialect_gaerdbms")
Note: 0.8 allows a dialect to be registered within the current process (as shown above). If you are running an older version of SQLAlchemy, I recommend upgrading to 0.8+ or you'll need to create a separate install for the dialect as outlined here.
3 - Update your create_engine('...')
url as the project and instance are now provided as part of the query string of the URL
mysql+gaerdbms:///?instance=
for example:
create_engine('mysql+gaerdbms:///mydb?instance=myproject:instance1')
I think I have a working sample script as follows. Can you try a similar thing and let me know how it goes?
However(and you might be already aware of it), if your goal is to create the schema on Google Cloud SQL instance, maybe you can create the schema against a local mysql server, dump it with mysqldump, and then import the schema to Google Cloud SQL. Using local mysql server is very convenient for development anyway.
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()