On Wed, May 15, 2019 at 5:23 AM Diego Quintana <daquinta...@gmail.com> wrote: > > Hello again! > > This worked, thanks! > > I noticed that if I use `bind.connection`, I do not need to call > `engine.raw_connection` anymore. I wonder why is this. Is this a proxy to a > raw connection or it was never necessary from the start?
"bind" is itself a Connection object in this case so you are in fact calling .connection from the Connection object. > > Thanks! > > Am Freitag, 10. Mai 2019 13:09:04 UTC+2 schrieb Diego Quintana: >> >> Hello! I'm trying to seed my database using my first migration script, and I >> have trouble understanding what `op.get_bind()` returns, and mainly the >> difference between `Connection` and `engine.connection()`. Say I have a >> migration script that creates a table called `cities` and then it performs a >> `COPY FROM CSV` using `engine.raw_connection`. >> >> The method `insert_default_csv` is failing in (note that line might not be >> the exact one) >> >> File "/src/app/migrations/versions/d054d8692328_initial_migration.py", >> line 91, in insert_default_csv >> cursor.copy_expert(cmd, f) >> psycopg2.ProgrammingError: relation "cities" does not exist >> >> This is probably due to the fact that the `bind` object the class `City` was >> created with and `bind.engine` used in that method are pointing to different >> places. This can be tested with the bit >> >> logger.info(bind.engine.connect() == bind) # INFO [alembic] False >> logger.info(bind.engine.dialect.has_table(bind.engine.connect(), >> "cities")) # INFO [alembic] False >> logger.info(bind.engine.dialect.has_table(bind, "cities")) # INFO >> [alembic] True >> >> >> In short what are the differences between these two objects and how can I >> access a working engine in Alembic from the context or the bind? >> >> >> I'm using >> >> Flask-SQLAlchemy==2.4.0 >> marshmallow-sqlalchemy==0.16.2 >> SQLAlchemy==1.3.3 >> alembic==1.0.10 >> >> >> Here is my migration script >> >> """Initial migration >> >> Revision ID: d054d8692328 >> Revises: >> Create Date: 2019-03-08 09:34:02.836061 >> >> """ >> import time >> import csv >> from flask_sqlalchemy import SQLAlchemy >> from datetime import datetime >> >> from pathlib import Path >> import logging >> >> from alembic import op, context >> import sqlalchemy as sa >> >> # revision identifiers, used by Alembic. >> revision = "d054d8692328" >> down_revision = None >> branch_labels = None >> depends_on = None >> >> DATA_FOLDER = Path.cwd().joinpath(f"migrations/data/{revision}/") >> logger = logging.getLogger("alembic") >> >> >> # ─── NON TEMPLATED IMPORTS >> ────────────────────────────────────────────────────────────── >> >> >> # ─── LOCAL MODEL DEFINITIONS >> ──────────────────────────────────────────────────── >> >> db = SQLAlchemy() >> >> class TerritoryMixin(object): >> """A Mixin to keep definitions DRY""" >> id = db.Column(db.Integer, primary_key=True) >> code = db.Column(db.Integer, nullable=False, unique=True) >> code_alias = db.Column(db.String(64), nullable=False, unique=True) >> name = db.Column(db.String(64), unique=True) >> region_code = db.Column(db.Integer, nullable=True) >> last_updated = db.Column(db.DateTime(), default=datetime.utcnow) >> >> class InsertionMixin(object): >> @classmethod >> def insert_default_elements(cls, session): >> """A local implementation of >> `my_app.models.mixins.BaseModelMixin.insert_defaults` >> >> This version does NOT check state, and is meant to be handled by >> alembic only. >> """ >> for element in cls._default_elements: >> # constructor call >> instance = cls(**element) >> session.add(instance) >> session.commit() >> >> @classmethod >> def insert_default_csv(cls, csv_path, engine): >> """A local implementation of >> `my_app.models.mixins.BaseModelMixin.insert_from_csv_psql_raw` >> >> This version does NOT check state and is meant to be handled by >> alembic only. >> """ >> >> SEP = ';' >> HEADER = True >> >> logger.debug('using engine: "%s"', engine) >> >> with open(csv_path, "rt") as f: >> columns = next(csv.reader(f))[0].split(SEP) >> logger.debug('columns: "%s"', columns) >> f.seek(0) >> >> coltxt = " (%s)" % ", ".join(columns) if columns else "" >> logger.debug('coltxt: "%s"', coltxt) >> >> conn = engine.raw_connection() >> cursor = conn.cursor() >> >> cursor.execute("SET datestyle = 'ISO,DMY'") >> >> cmd = "COPY %s%s FROM STDIN DELIMITER '%s' CSV %s" % ( >> cls.__tablename__, >> coltxt, >> SEP, >> "HEADER" if HEADER else "", ) # \COPY instead of COPY is >> preferred >> >> logger.debug('SQL query: "%s"', cmd) >> >> cursor.copy_expert(cmd, f) >> >> # commit the query >> t0 = time.time() >> conn.commit() >> >> # take the file pointer back to the beginning so we can read it >> again >> f.seek(0) >> logger.info( >> "Table '%s': %i row(s) inserted in %.1f seconds.", >> cls.__tablename__, >> sum(1 for row in csv.reader(f)), time.time() - t0, ) >> >> >> >> class City(InsertionMixin, TerritoryMixin, db.Model): >> __tablename__ = "cities" >> # users = backref from many to many relationship with User model >> >> _default_csv_filename = DATA_FOLDER.joinpath('cities.csv') >> >> >> >> # ─── END OF LOCAL MODEL DEFINITIONS >> ───────────────────────────────────────────── >> >> >> # ─── BEGIN OF ALEMBIC COMMANDS >> ────────────────────────────────────────────────── >> >> >> def upgrade(): >> schema_upgrades() >> data_upgrades() >> >> >> def downgrade(): >> schema_downgrades() >> data_downgrades() >> >> >> def schema_upgrades(): >> bind = op.get_bind() >> session = sa.orm.Session(bind=bind) >> >> City.__table__.create(bind) >> >> >> >> def schema_downgrades(): >> op.drop_table("cities") >> >> >> def data_upgrades(): >> bind = op.get_bind() >> session = sa.orm.Session(bind=bind) >> >> # ─── INSERT DATA DEFINED LOCALLY >> ──────────────────────────────────────────────── >> >> logger.info(bind) # INFO [alembic] <sqlalchemy.engine.base.Connection >> object at 0x7fb78b1cc518> >> logger.info(session) # INFO [alembic] <sqlalchemy.orm.session.Session >> object at 0x7fb77b0b5eb8> >> logger.info(bind.engine) # INFO [alembic] >> Engine(postgresql+psycopg2://lap:***@postgres:5432/lap) >> >> # https://groups.google.com/forum/#!topic/sqlalchemy/PsBfyWiBgBY >> logger.info(bind.engine.connect() == bind) # INFO [alembic] False >> logger.info(bind.engine.dialect.has_table(bind.engine.connect(), >> "cities")) # INFO [alembic] False >> logger.info(bind.engine.dialect.has_table(bind, "cities")) # INFO >> [alembic] True >> >> City.insert_default_csv(City._default_csv_filename, engine=bind.engine) >> # fails with "cities" table does not exist, WHY? >> >> >> >> def data_downgrades(): >> bind = op.get_bind() >> session = sa.orm.Session(bind=bind) >> >> session.drop_all() >> >> >> # ─── END OF ALEMBIC COMMANDS >> ──────────────────────────────────────────────────── >> >> >> Thanks! > > -- > SQLAlchemy - > The Python SQL Toolkit and Object Relational Mapper > > http://www.sqlalchemy.org/ > > To post example code, please provide an MCVE: Minimal, Complete, and > Verifiable Example. See http://stackoverflow.com/help/mcve for a full > description. > --- > You received this message because you are subscribed to the Google Groups > "sqlalchemy" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to sqlalchemy+unsubscr...@googlegroups.com. > To post to this group, send email to sqlalchemy@googlegroups.com. > Visit this group at https://groups.google.com/group/sqlalchemy. > To view this discussion on the web visit > https://groups.google.com/d/msgid/sqlalchemy/bf1e5680-18a4-4387-9639-2f3abe6d194e%40googlegroups.com. > For more options, visit https://groups.google.com/d/optout. -- SQLAlchemy - The Python SQL Toolkit and Object Relational Mapper http://www.sqlalchemy.org/ To post example code, please provide an MCVE: Minimal, Complete, and Verifiable Example. See http://stackoverflow.com/help/mcve for a full description. --- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. Visit this group at https://groups.google.com/group/sqlalchemy. To view this discussion on the web visit https://groups.google.com/d/msgid/sqlalchemy/CA%2BRjkXHCfHd5frL1Ww8PXWwoDdumx64LkcJRO93vpQwuVyDJSg%40mail.gmail.com. For more options, visit https://groups.google.com/d/optout.