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?
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 [email protected].
To post to this group, send email to [email protected].
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.