web-dev-qa-db-fra.com

SqlAlchemy équivalent de la chaîne de connexion pyodbc utilisant FreeTDS

Les travaux suivants:

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

Les échecs suivants:

import sqlalchemy
sqlalchemy.create_engine("mssql://myuser:[email protected]:1433/mydb?driver=FreeTDS& odbc_options='TDS_Version=8.0'").connect()

Le message d'erreur ci-dessus est:

DBAPIError: (Error) ('08001', '[08001] [unixODBC] [FreeTDS] [SQL Server] Impossible de se connecter à la source de données (0) (SQLDriverConnectW)') Aucun Aucun

Quelqu'un peut-il s'il vous plaît me diriger dans la bonne direction? Existe-t-il un moyen de dire simplement à sqlalchemy de transmettre une chaîne de connexion spécifique à pyodbc?

Veuillez noter: Je veux garder ce DSN-less.

26
mwolfe02

L'exemple de @Singletoned ne fonctionnerait pas pour moi avec SQLAlchemy 0.7.2. À partir de documentation SQLAlchemy pour la connexion à 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.

Donc, pour le faire fonctionner, j'ai utilisé:

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))

Cela devrait également s'appliquer à Sybase.

REMARQUE: Dans python 3, le module urllib a été divisé en parties et renommé. Ainsi, cette ligne dans python 2.7:

quoted = urllib.quote_plus

doit être remplacé par cette ligne en python3:

quoted = urllib.parse.quote_plus
39
jmagnusson

Je suis toujours intéressé par un moyen de le faire en une seule ligne dans le sqlalchemy create_engine instruction, mais j'ai trouvé la solution de contournement suivante détaillée ici :

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)

[~ # ~] update [~ # ~] : répond à un problème que j'ai soulevé dans mon propre commentaire concernant l'impossibilité de transmettre des arguments à la chaîne de connexion . Voici une solution générale si vous devez vous connecter dynamiquement à différentes bases de données au moment de l'exécution. Je ne transmets que le nom de la base de données en tant que paramètre, mais des paramètres supplémentaires pourraient facilement être utilisés au besoin:

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://stackoverflow.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))
21
mwolfe02

Cela marche:

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

Dans ce format, SQLAlchemy ignore simplement la chaîne de connexion et la transmet directement à pyodbc.

Mettre à jour:

Désolé, j'ai oublié que l'URI doit être encodé en URL, par conséquent, le fonctionnement suivant:

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()
5
Singletoned

En interne, "my.db.server: 1433" est transmis dans le cadre d'une chaîne de connexion comme SERVER=my.db.server:1433;.

Malheureusement, unixODBC/FreeTDS n'acceptera pas de port dans le bit SERVER. Au lieu de cela, il veut SERVER=my.db.server;PORT=1433;

Pour utiliser la syntaxe sqlalchemy pour une chaîne de connexion, vous devez spécifier le port en tant que paramètre.

sqlalchemy.create_engine("mssql://myuser:[email protected]:1433/mydb?driver=FreeTDS& odbc_options='TDS_Version=8.0'").connect()

devient:

sqlalchemy.create_engine("mssql://myuser:[email protected]/mydb?driver=FreeTDS&port=1433& odbc_options='TDS_Version=8.0'").connect()
4
grapier

Pour passer divers paramètres à votre fonction de connexion, il semble que la chaîne de formatage fasse ce que vous voulez:

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))

Et vous l'appeleriez alors avec quelque chose comme:

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

Plus d'informations sur les chaînes de format sont ici: http://docs.python.org/library/string.html#formatstrings

0
skermajo