Je voudrais créer une table MySQL avec la fonction to_sql de Pandas qui a une clé primaire (il est généralement bon d'avoir une clé primaire dans une table mysql) comme suit:
group_export.to_sql(con = db, name = config.table_group_export, if_exists = 'replace', flavor = 'mysql', index = False)
mais cela crée une table sans clé primaire (ou même sans index).
La documentation mentionne le paramètre 'index_label' qui combiné avec le paramètre 'index' pourrait être utilisé pour créer un index mais ne mentionne aucune option pour les clés primaires.
Avertissement: cette réponse est plus expérimentale que pratique, mais mérite peut-être d'être mentionnée.
J'ai trouvé que la classe pandas.io.sql.SQLTable
A nommé l'argument key
et si vous lui affectez le nom du champ, ce champ devient la clé primaire:
Malheureusement, vous ne pouvez pas simplement transférer cet argument depuis la fonction DataFrame.to_sql()
. Pour l'utiliser, vous devez:
créer une instance de pandas.io.SQLDatabase
engine = sa.create_engine('postgresql:///somedb')
pandas_sql = pd.io.sql.pandasSQL_builder(engine, schema=None, flavor=None)
définir une fonction analogue à pandas.io.SQLDatabase.to_sql()
mais avec un argument *kwargs
supplémentaire qui est passé à l'objet pandas.io.SQLTable
créé à l'intérieur (je viens de copier la méthode to_sql()
d'origine et d'ajouter *kwargs
):
def to_sql_k(self, frame, name, if_exists='fail', index=True,
index_label=None, schema=None, chunksize=None, dtype=None, **kwargs):
if dtype is not None:
from sqlalchemy.types import to_instance, TypeEngine
for col, my_type in dtype.items():
if not isinstance(to_instance(my_type), TypeEngine):
raise ValueError('The type of %s is not a SQLAlchemy '
'type ' % col)
table = pd.io.sql.SQLTable(name, self, frame=frame, index=index,
if_exists=if_exists, index_label=index_label,
schema=schema, dtype=dtype, **kwargs)
table.create()
table.insert(chunksize)
appelez cette fonction avec votre instance SQLDatabase
et la trame de données que vous souhaitez enregistrer
to_sql_k(pandas_sql, df2save, 'tmp',
index=True, index_label='id', keys='id', if_exists='replace')
Et nous obtenons quelque chose comme
CREATE TABLE public.tmp
(
id bigint NOT NULL DEFAULT nextval('tmp_id_seq'::regclass),
...
)
dans la base de données.
PS Vous pouvez bien sûr utiliser les fonctions monkey-patch DataFrame
, io.SQLDatabase
Et io.to_sql()
pour utiliser cette solution de contournement avec commodité.
Ajoutez simplement la clé primaire après avoir téléchargé la table avec des pandas.
group_export.to_sql(con=engine, name=example_table, if_exists='replace',
flavor='mysql', index=False)
with engine.connect() as con:
con.execute('ALTER TABLE `example_table` ADD PRIMARY KEY (`ID_column`);')
automap_base
de sqlalchemy.ext.automap
(tableNamesDict est un dict avec seulement les tables Pandas):
metadata = MetaData()
metadata.reflect(db.engine, only=tableNamesDict.values())
Base = automap_base(metadata=metadata)
Base.prepare()
Ce qui aurait parfaitement fonctionné, sauf pour un problème, la carte automatique nécessite que les tables aient une clé primaire. Ok, pas de problème, je suis sûr Pandas to_sql
a un moyen d'indiquer la clé primaire ... non. C'est là que ça devient un peu hacky:
for df in dfs.keys():
cols = dfs[df].columns
cols = [str(col) for col in cols if 'id' in col.lower()]
schema = pd.io.sql.get_schema(dfs[df],df, con=db.engine, keys=cols)
db.engine.execute('DROP TABLE ' + df + ';')
db.engine.execute(schema)
dfs[df].to_sql(df,con=db.engine, index=False, if_exists='append')
J'itère le dict
de DataFrames
, j'obtiens une liste des colonnes à utiliser pour la clé primaire (c'est-à-dire celles contenant id
), j'utilise get_schema
pour créer les tables vides, puis ajoutez le DataFrame
à la table.
Maintenant que vous avez les modèles, vous pouvez les nommer et les utiliser explicitement (c'est-à-dire User = Base.classes.user
) avec session.query
ou créez un dict de toutes les classes avec quelque chose comme ceci:
alchemyClassDict = {}
for t in Base.classes.keys():
alchemyClassDict[t] = Base.classes[t]
Et interrogez avec:
res = db.session.query(alchemyClassDict['user']).first()