SqlAlchemy 等效于使用 FreeTDS 的 pyodbc 连接字符串

Posted

技术标签:

【中文标题】SqlAlchemy 等效于使用 FreeTDS 的 pyodbc 连接字符串【英文标题】:SqlAlchemy equivalent of pyodbc connect string using FreeTDS 【发布时间】:2011-05-28 11:23:59 【问题描述】:

以下作品:

import pyodbc
pyodbc.connect('DRIVER=FreeTDS;Server=my.db.server;Database=mydb;UID=myuser;PWD=mypwd;TDS_Version=8.0;Port=1433;')

以下失败:

import sqlalchemy
sqlalchemy.create_engine("mssql://myuser:mypwd@my.db.server:1433/mydb?driver=FreeTDS& odbc_options='TDS_Version=8.0'").connect()

上面的错误信息是:

DBAPIError: (Error) ('08001', '[08001] [unixODBC][FreeTDS][SQL Server]无法连接到数据源 (0) (SQLDriverConnectW)') 无 无

有人可以指出我正确的方向吗?有没有一种方法可以简单地告诉 sqlalchemy 将特定的连接字符串传递给 pyodbc?

请注意:我想保留此 DSN-less。

【问题讨论】:

【参考方案1】:

我仍然对在 sqlalchemy create_engine 语句中的一行中执行此操作的方法感兴趣,但我找到了以下解决方法 detailed here:

import pyodbc, sqlalchemy

def connect():
    pyodbc.connect('DRIVER=FreeTDS;Server=my.db.server;Database=mydb;UID=myuser;PWD=mypwd;TDS_Version=8.0;Port=1433;')

sqlalchemy.create_engine('mssql://', creator=connect)

更新:解决了我在自己的评论中提出的关于无法将参数传递给连接字符串的问题。如果您需要在运行时动态连接到不同的数据库,下面是一个通用的解决方案。我只将数据库名称作为参数传递,但可以根据需要轻松使用其他参数:

import pyodbc
import os

class Creator:
    def __init__(self, db_name='MyDB'):
        """Initialization procedure to receive the database name"""
        self.db_name = db_name

    def __call__(self):
        """Defines a custom creator to be passed to sqlalchemy.create_engine
           http://***.com/questions/111234/what-is-a-callable-in-python#111255"""
        if os.name == 'posix':
            return pyodbc.connect('DRIVER=FreeTDS;'
                                  'Server=my.db.server;'
                                  'Database=%s;'
                                  'UID=myuser;'
                                  'PWD=mypassword;'
                                  'TDS_Version=8.0;'
                                  'Port=1433;' % self.db_name)
        elif os.name == 'nt':
            # use development environment
            return pyodbc.connect('DRIVER=SQL Server;'
                                  'Server=127.0.0.1;'
                                  'Database=%s_Dev;'
                                  'UID=user;'
                                  'PWD=;'
                                  'Trusted_Connection=Yes;'
                                  'Port=1433;' % self.db_name)

def en(db_name):
    """Returns a sql_alchemy engine"""
    return sqlalchemy.create_engine('mssql://', creator=Creator(db_name))

【讨论】:

我现在意识到这有一个限制,即不允许以简单的方式将诸如数据库名称之类的内容作为参数传递给连接函数。这在 python 中可能是可能的,但我不确定我会怎么做(lambda 表达式?)。 请参阅上面的更新以回应我之前的担忧。 太棒了!尝试让带有 unixODBC 连接池的 DB2 和 pyodbc 工作起来,真是费了一番周折。与ibm-db-sa-py3 一起使用它比ibm 驱动程序好得多。谢谢=)【参考方案2】:

要将各种参数传递给你的连接函数,听起来格式字符串可能会做你想做的事:

def connect(server, dbname, user, pass):
  pyodbc.connect('DRIVER=FreeTDS;Server=%s;Database=%s;UID=%s;PWD=%s;TDS_Version=8.0;Port=1433;' % (server, dbname, user, pass))

然后你会用类似这样的方式调用它:

connect('myserver', 'mydatabase', 'myuser', 'mypass')

有关格式字符串的更多信息在这里:http://docs.python.org/library/string.html#formatstrings

【讨论】:

请重新阅读我的问题。 pyodbc.connect 代码是一个工作示例。我的问题是如何将 pyodbc.connect 字符串转换为 sqlalchemy 可以正确传递给 pyodbc 的格式。 是的,这个答案是为了回应您在 12 月 20 日关于无法轻松将参数传递给您的工作 connect() 示例的评论。回想起来,我可能应该在 cmets 上发帖道歉——这是我的第一次。 不用担心。我希望我的评论语气不会那么刺耳——我不是故意的。我不希望您在 SO 的第一次经历是糟糕的经历。这里的社区总体上非常友好。我希望你能坚持下去!【参考方案3】:

这行得通:

import sqlalchemy
sqlalchemy.create_engine("DRIVER=FreeTDS;Server=my.db.server;Database=mydb;UID=myuser;PWD=mypwd;TDS_Version=8.0;Port=1433;").connect()

在这种格式中,SQLAlchemy 只是忽略连接字符串并将其直接传递给 pyodbc。

更新:

抱歉,我忘记了 uri 必须是 url 编码的,因此,以下工作:

import sqlalchemy
sqlalchemy.create_engine("DRIVER%3D%7BFreeTDS%7D%3BServer%3Dmy.db.server%3BDatabase%3Dmydb%3BUID%3Dmyuser%3BPWD%3Dmypwd%3BTDS_Version%3D8.0%3BPort%3D1433%3B").connect()

【讨论】:

我不再在我的项目中使用 SQLAlchemy,所以我相信你的话,这是可行的。它肯定比我试图做的要简单得多。我不知道为什么我在最初进行故障排除时没有考虑尝试这样做。 它没有:sqlalchemy.exc.ArgumentError: Could not parse rfc1738 URL from string 'DRIVER%3D%7BFreeTDS%7D%3BServer%3Dmy.db.server%3BDatabase%3Dmydb%3BUID%3Dmyuser%3BPWD%3Dmypwd%3BTDS_Version%3D8.0%3BPort%3D1433%3B' 你是对的。似乎在过去 1251 天的某个时间点,格式发生了变化。【参考方案4】:

@Singletoned 的示例不适用于 SQLAlchemy 0.7.2。来自SQLAlchemy docs for connecting to SQL Server:

If you require a connection string that is outside the options presented above, use the odbc_connect keyword to pass in a urlencoded connection string. What gets passed in will be urldecoded and passed directly.

所以为了让它发挥作用,我使用了:

import urllib
quoted = urllib.quote_plus('DRIVER=FreeTDS;Server=my.db.server;Database=mydb;UID=myuser;PWD=mypwd;TDS_Version=8.0;Port=1433;')
sqlalchemy.create_engine('mssql+pyodbc:///?odbc_connect='.format(quoted))

这也应该适用于 Sybase。

注意:在 python 3 中,urllib 模块已被拆分为多个部分并重命名。因此,python 2.7 中的这一行:

quoted = urllib.quote_plus

在python3中必须改成这一行:

quoted = urllib.parse.quote_plus

【讨论】:

我花了几个小时在 OSX Mavericks 上玩弄 iODBC,这最终是我需要让 pyodbc、iODBC 和 SQLAlchemy 一起工作的答案。 当然是3个斜线!为什么我以前从来没想过! 谢谢@jmagnusson。 此解决方案在 Python2.7 上运行良好,但在 Python3 (mysql) 上似乎不适用于我。从错误消息来看,连接元素名称似乎没有正确地从 pyodbc 传递到 create_engine。 @jonathanrocher 这在 python3 中对我有用。但我不得不将代码的 urllib 部分更改为: urllib.parse.quote_plus 此解决方案适用于没有 dns 名称的 Sybase ASE。【参考方案5】:

在内部,“my.db.server:1433”作为连接字符串的一部分传递,例如SERVER=my.db.server:1433;

不幸的是 unixODBC/FreeTDS 不接受 SERVER 位中的端口。相反,它想要SERVER=my.db.server;PORT=1433;

要对连接字符串使用 sqlalchemy 语法,您必须将端口指定为参数。

sqlalchemy.create_engine("mssql://myuser:mypwd@my.db.server:1433/mydb?driver=FreeTDS& odbc_options='TDS_Version=8.0'").connect()

变成:

sqlalchemy.create_engine("mssql://myuser:mypwd@my.db.server/mydb?driver=FreeTDS&port=1433& odbc_options='TDS_Version=8.0'").connect()

【讨论】:

以上是关于SqlAlchemy 等效于使用 FreeTDS 的 pyodbc 连接字符串的主要内容,如果未能解决你的问题,请参考以下文章

Postgres中'money'和'OID'的sqlalchemy等效列类型是啥?

sqlalchemy 无法连接到 ms sql server

SQLAlchemy 是不是与 Django 的 get_or_create 等效?

SQLAlchemy 是不是与 Django 的 get_or_create 等效?

如何使用 SqlAlchemy 按 id 查询数据库?

Sqlalchemy + pymssql 连接失败