I am working on Alembic with SQLAlchemy on my Alembic.ini I am setting my postgres connection string but it goes to my public schema, i need to choose my schema.
How I can use an specific schema:
alembic.ini file
sqlalchemy.url = postgresql://myuser:mypassword#server:host/database
models.py
import datetime
from sqlalchemy import Column,Integer,String,DateTime,create_engine
from sqlalchemy.orm import declarative_base,sessionmaker
Base = declarative_base()
class UserModel(Base):
__tablename__='myschema.person'
id = Column(Integer, primary_key=True)
first_name = Column(String, nullable=False)
last_name = Column(String, nullable = False)
birth = Column(DateTime)
This connection string works with public, I need to connect to my "sales" schema.
On my models.py I am just creating a table person, I tried adding there Core schema but didn't works
Regards
Here you can find a complete working example
Define schema on your model
class A(Base):
__tablename__ = 'a'
__table_args__ = {
"schema": "alphabet"
}
edit env.py to work with schemas
with connectable.connect() as connection:
"""
Configure migration context
1. Pass our models metadata
2. Set schema for alembic_version table
3. Load all available schemas
"""
context.configure(
connection=connection,
target_metadata=target_metadata,
"""
here you are passing schema
"""
version_table_schema=target_metadata.schema,
include_schemas=True
)
Run alembic revision and upgrade and it will work
Related
I'm having problems trying to add inherited objects with SQLAlchemy following the instructions in https://docs.sqlalchemy.org/en/14/orm/inheritance.html to implement Joined Table Inheritance.
I'm using a PostgreSQL version 14 as a database engine.
Here is my Base configuration:
import os
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.automap import automap_base
db_string = os.environ['DB_STRING']
engine = create_engine(db_string)
db_session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
Base = automap_base()
Base.query = db_session.query_property()
Base.prepare(engine, reflect=True)
Here is Instrument (parent class) definition:
from sqlalchemy import Column, String
from context_model.database import Base
class Instrument(Base):
__tablename__ = "instrument"
_id = Column(String, primary_key=True)
discriminator = Column(String)
__mapper_args__ = {
'polymorphic_identity': 'instrument',
'polymorphic_on': discriminator
Here is Bond (child class) definition:
import datetime as dt
from sqlalchemy import Column, Integer, Float, String, DateTime, ForeignKey
from sqlalchemy.orm import relationship
from context_model.instrument.instrument import Instrument
class Bond(Instrument):
__tablename__ = "bond"
_id = Column(String, ForeignKey("instrument._id"), primary_key=True)
_provider_bbg_id = Column(String)
__mapper_args__ = {
'polymorphic_identity': 'bond'
}
When I try to add a Bond instance and persist it to the database:
Bond = Base.classes.bond
bond = Bond()
bond._id = "0"
bond._provider_bbg_id = "XXX"
db_session.add(bond)
db_session.commit()
appears the following error message:
sqlalchemy.exc.IntegrityError: (psycopg2.errors.ForeignKeyViolation) insert or update on table "bond" violates foreign key constraint "bond__id_fkey"
DETAIL: Key (_id)=(0) is not present in table "instrument"."
It seems to me that the inheritance is not working for some reason, did I define well the Instrument (parent) and Bond (child) classes ?, maybe do I need to use another type of Base ? (I'm using automap_base )
Thanks in advance for your help!
I don't know the exact reasons but I changed from declarative_base() to automap_base() as #snakecharmerb suggested in the commentaries and now it works :)
Mssql+pyodbc connection.
Imports and db
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, Table, text, Text, Unicode, MetaData
from sqlalchemy.orm import relationship
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
I have generated table models using flask-sqlacodegen which look like this
class BlaBla(db.Model):
__tablename__ = 'bla_bla'
blabla= db.Column(db.Integer, primary_key=True)
bla2= db.Column(db.Integer, nullable=False)
The view that I have in the db was generated like this
t_bla_bla_bla_view = Table(
'View_bla_bla_bla',
Column('irrelevant', db.Unicode(50), nullable=False),
Column('etc', db.Unicode(50), nullable=False),
Column('etc2', db.Unicode(50), nullable=False)
)
When I run flask db migrate, the tables are properly created, while the View is also created as a table.
What is the actual syntax on creating a view?
Maybe there is a raw query migrate workaround?
I'm trying to create a table with geography data using geoalchemy2 with sqlalchemy following the geoalchemy ORM tutorial
I have the postgis extension installed on the schema I'm using. The user I'm connecting with is owner of the schema.
When I run the python code:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from geoalchemy2 import Geography
import src.config as config
Base = declarative_base()
class Lake(Base):
__tablename__ = 'lake'
__table_args__ = ({'schema': 'geobox'})
id = Column(Integer, primary_key=True)
country_iso_code = Column(String(2))
zone_code = Column(String(45))
zone_type = Column(String(45))
zone_test = Column(Geography('POINT'))
from sqlalchemy import create_engine
engine = create_engine('postgres://{user}:{pwd}#{host}:{port}/{database}'.format(
user=config.DbConfig().username,
pwd=config.DbConfig().password,
host=config.DbConfig().host,
port=config.DbConfig().port,
database=config.DbConfig().database), echo=True)
Lake.__table__.create(engine)
I get the error:
sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) type "geography" does not exist
What am I missing here ?
I found out the search_path was not properly set, since the postgis had been installed at the table level and not at the schema level.
Not related to sqlalchemy then. I just needed to change the search_path to let pg find the table where postgis is installed with:
ALTER ROLE "my_role_name" SET search_path TO 'my_table_name',"$user",public;
(assuming my_table_name is the name of the table where the postgis extension has been installed)
The Alembic migration script :
def upgrade():
uuid_gen = saexp.text("UUID GENERATE V1MC()")
op.create_table(
'foo',
sa.Column('uuid', UUID, primary_key=True, server_default=uuid_gen),
sa.Column(
'inserted',
sa.DateTime(timezone=True),
server_default=sa.text("not null now()"))
sa.Column('data', sa.Text)
)
This is my Base class for SQL Alchemy:
Class Foo(Base):
__tablename__ = 'foo'
inserted = Column(TIMESTAMP)
uuid = Column(UUID, primary_key=True)
data = Column(TEXT)
It has a static mehtod for insert :
#staticmethod
def insert(session, jsondata):
foo = Foo()
foo.data = jsondata['data']
if 'inserted' in jsondata:
foo.inserted = jsondata['inserted']
if 'uuid' in jsondata:
foo.uuid = jsondata['uuid']
session.add(foo)
return foo
the purpose of the 2 if's are to simplify testing. this way i can "inject" a uuid and inserted date, to get predictible data for my tests
When trying to insert data
foo = Foo()
foo.insert(session, {"data": "foo bar baz"})
session.commit()
I get an IntegrityError :
[SQL: 'INSERT INTO foo (inserted, data) VALUES (%(inserted)s, %(data)s) RETURNING foo.uuid'] [parameters: {'data': 'foo bar baz', 'inserted': None}]
wich seem normal to me because the insert violates the "not-null" constraint in the postgres database.
How do I prevent sql alchemy from inserting the None value to the inserted field ?
While playing and testing around, I found that if the "inserted" column is defined as primary key , sql alchemy does not include the field in the insert statement.
def upgrade():
uuid_gen = saexp.text("UUID GENERATE V1MC()")
op.create_table(
'foo',
sa.Column('uuid', UUID, primary_key=True, server_default=uuid_gen),
sa.Column(
'inserted',
primary_key=True,
sa.DateTime(timezone=True),
server_default=sa.text("not null now()"))
sa.Column('data', sa.Text)
)
But this is not what I want.
The primary problem is the server_default which is missing in the inserted member in class Foo. It's only present in the alembic script. Note that the alembic definitions are only used when running the migrations. They do not affect the application. For this reason, it's a good idea to copy the exact same definitions from the alembic script to your application (or vice-versa).
Because no value is defined in the model definition, sqlalchemy seems to set this to None when the class is instantiated. This will then be sent to the DB which will complain. To fix this, either set default or server_default on the model definition (the class inheriting from Base).
Some additional notes/questions:
Where does UUID GENERATE V1MC() come from? The official docs look different. I replaced it with func.uuid_generate_v1mc().
The server_default value in your case contains not null which is incorrect. You should set nullable=False on you column attribute (see below).
alembic script
# revision identifiers, used by Alembic.
revision = THIS_IS_DIFFERENT_ON_EACH_INSTANCE! # '1b7e145f2138'
down_revision = None
branch_labels = None
depends_on = None
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import UUID
def upgrade():
op.create_table(
'foo',
sa.Column('uuid', UUID, primary_key=True,
server_default=sa.func.uuid_generate_v1mc()),
sa.Column(
'inserted',
sa.DateTime(timezone=True),
nullable=False,
server_default=sa.text("now()")),
sa.Column('data', sa.Text)
)
def downgrade():
op.drop_table('foo')
tester.py
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, create_engine, func
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.dialects.postgresql import (
TEXT,
TIMESTAMP,
UUID,
)
engine = create_engine('postgresql://michel#/michel')
Session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
Base = declarative_base()
class Foo(Base):
__tablename__ = 'foo'
inserted = Column(TIMESTAMP, nullable=False,
server_default=func.now())
uuid = Column(UUID, primary_key=True,
server_default=func.uuid_generate_v1mc()),
data = Column(TEXT)
#staticmethod
def insert(session, jsondata):
foo = Foo()
foo.data = jsondata['data']
if 'inserted' in jsondata:
foo.inserted = jsondata['inserted']
if 'uuid' in jsondata:
foo.uuid = jsondata['uuid']
session.add(foo)
return foo
if __name__ == '__main__':
session = Session()
Foo.insert(session, {"data": "foo bar baz"})
session.commit()
session.close()
output after execution
[9:43:54] michel#BBS-nexus [1 background job(s)]
/home/users/michel/tmp› psql -c "select * from foo"
uuid | inserted | data
--------------------------------------+-------------------------------+-------------
71f5fd32-0602-11e6-aebb-27be4bbac26e | 2016-04-19 09:43:45.297191+02 | foo bar baz
(1 row)
I'm using SQLAlchemy core with a postgresql database and I would like to add the ENUM type to my table definition. According to the postgresql documentation, the ENUM type must be defined prior to the table being created:
CREATE TYPE gender_enum AS ENUM ('female', 'male');
CREATE TABLE person (
name VARCHAR(20),
gender gender_enum
);
The problem is when I'm creating the table definition. After reading the SQLAlchemy documentation I couldn't find any implementation examples. I've tried something like this but it didn't work:
from sqlalchemy.dialects.postgresql import ENUM
person = Table('user_profile', metadata,
Column('name', String(20)),
Column('gender', ENUM('female', 'male'))
);
How it must be done?
You need to import Enum from sqlalchemy and add a name to it. It should work like this:
from sqlalchemy import Enum
person = Table("user_profile", metadata,
Column("name", String(20)),
Column("gender", Enum("female", "male", name="gender_enum", create_type=False))
);
#Tim's answer is definitely correct but I wanted to offer the way I setup my ENUMs.
In my models.py I will create the values as tuples
skill_levels = ('Zero', 'A little', 'Some', 'A lot')
Then I will create a skill_level_enum variable and assign it an ENUM class with the skill level values as args.
skill_level_enum = ENUM(*skill_levels, name="skill_level")
In my table model then I pass in the skill_level_enum
class User(db.Model):
id = db.Column(db.Integer(), primary_key=True)
skill_level = db.Column(skill_level_enum)
I have found this makes my code a lot cleaner and I'm able to make updates to the skill_levels at the top of my file rather than scanning my models for the right thing to update.
You can use Python's native enums as the column type as well:
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.dialects.postgresql import ENUM as pgEnum
from enum import Enum, unique
#unique
class errorTypeEnum(Enum):
videoValidation = 'videoValidation'
audioValidation = 'audioValidation'
subtitleValidation = 'subtitleValidation'
db = SQLAlchemy()
class Error(db.Model):
serviceID = db.Column(db.String(20), primary_key=True)
timestamp = db.Column(db.DateTime, unique=False, nullable=False)
category = db.Column(pgEnum(errorTypeEnum), unique=False, nullable=False)
The code below worked for me on SQLAlchemy 1.3.11 and Postgres 12.0.
You must first create the Enum type in postgres before creating the user table. This can be done directly through sql statement.
CREATE TYPE permission AS ENUM ('READ_ONLY', 'READ_WRITE', 'ADMIN', 'OWNER');
Then set up the project model
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model):
__tablename__ = 'user'
user_id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(200), nullable=False)
access = db.Column(db.Enum('READ_ONLY', 'READ_WRITE', 'ADMIN', 'OWNER', name="permission"))