SQLALCHEMY CONNECTION TWO DATABASE PROBLEM - sqlalchemy

Hello I have little problem,So need Help to connect two tables in database. I used ORM in python and I know how to connect it in main.py file.But I decided to make one file per table,so when i want to use foreign key it say's error. Here is the code which i used for connection two tables:
engine = create_engine('mysql+pymysql://root:#localhost/popis?charset=utf8mb4', echo=False)
Session = sessionmaker(bind=engine)
session = Session()
Base = declarative_base()
class Stanovnik(Base):
__tablename__ = "stanovnik"
id = Column(Integer, primary_key=True)
ime = Column(String(20))
prezime = Column(String(50))
ratni_staz = Column(Integer)
godine = Column(Integer)
broj_clanova=Column(Integer)
sifra_adrese = Column(Integer,ForeignKey(adresa.sifra_adrese))
sifra_zene = Column(Integer,ForeignKey(zena.sifra_zene))
sifra_ostali = Column(Integer,ForeignKey(ostali.sifra_ostali))
Base.metadata.create_all(engine)
I tried every method for sql but one thing is missing I think.

Related

In SQLAlchemy, how should I specify that the relationship field is required?

I have a model that depends on some fields on another model. This fields should be present when the record is created, but I do not see a way to enforce that on the database:
class Study(db.Model):
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
type = db.Column(Enum(StudyTypeChoices), nullable=False)
owner_id = db.Column(UUID(as_uuid=True), db.ForeignKey('owner.id'), nullable=False)
participants = db.relationship('Participant', lazy=True, cascade='save-update, merge, delete')
How can I make sure that 'participants' is provided when the Study record gets created (similar to what happens with the 'type' field)? I know I can put a wrapper around it to make sure of that, but I am wondering is there is a more neat way of doing it with sqlalchemy.
Edit: This is the definition of the Participant model
class Participant(UserBase):
id = db.Column(UUID(as_uuid=True), db.ForeignKey("user_base.id"), primary_key=True)
study_id = db.Column(UUID(as_uuid=True), db.ForeignKey('study.id'))
You can listen to before_flush events and prevent flushes containing studies without participants by raising an exception for instance.
#event.listens_for(Session, "before_flush")
def before_flush(session, flush_context, instances):
for instance in session.new: # might want to inspect session.dirty as well
if isinstance(instance, Study) and (
instance.participants is None or instance.participants == []
):
raise ValueError(
f"Study {instance} cannot have {instance.participants} participants."
)
This only checks for new studies, you might want to check in session.dirty as well for updated studies.
Full demo:
from sqlalchemy import Column, ForeignKey, Integer, create_engine, event
from sqlalchemy.orm import Session, declarative_base, relationship
Base = declarative_base()
class Study(Base):
__tablename__ = "study"
id = Column(Integer, primary_key=True)
participants = relationship("Participant", uselist=True, back_populates="study")
class Participant(Base):
__tablename__ = "participant"
id = Column(Integer, primary_key=True)
study_id = Column(Integer, ForeignKey("study.id"), nullable=True)
study = relationship("Study", back_populates="participants")
#event.listens_for(Session, "before_flush")
def before_flush(session, flush_context, instances):
for instance in session.new: # might want to inspect session.dirty as well
if isinstance(instance, Study) and (
instance.participants is None or instance.participants == []
):
raise ValueError(
f"Study {instance} cannot have {instance.participants} participants."
)
engine = create_engine("sqlite://", future=True, echo=True)
Base.metadata.create_all(engine)
s1 = Study()
p1_1 = Participant()
p1_2 = Participant()
s1.participants.extend([p1_1, p1_2])
s2 = Study()
with Session(bind=engine) as session:
session.add(s1)
session.commit() # OK
with Session(bind=engine) as session:
session.add(s2)
session.commit() # ValueError

When attempting to loaddata - get error problem installing fixture

I am trying to load data from a db.json file I created (Sqlite db was the source). The original database is Sqlite, and I am try to migrate to Mysql.
Everything works fine under Sqlite. I get the following error:
django.db.utils.OperationalError: Problem installing fixture '/home/balh/blah/db.json': Could not load trackx_site.Segment(pk=1): (1054, "Unknown column 'program_id' in 'field list'")
It seems like the Mysql tables do not have the foreign key or something?.... The models look like this:
class Program(models.Model):
air_date = models.DateField(default="0000/00/00")
air_time = models.TimeField(default="00:00:00")
service = models.CharField(max_length=10)
block_time = models.TimeField(default="00:00:00")
block_time_delta = models.DurationField(default=timedelta)
running_time = models.TimeField(default="00:00:00",blank=True)
running_time_delta = models.DurationField(default=timedelta)
remaining_time =
models.TimeField(default="00:00:00",blank=True)
remaining_time_delta = models.DurationField(default=timedelta)
title = models.CharField(max_length=190)
locked_flag = models.BooleanField(default=False)
locked_expiration = models.DateTimeField(null=True,default=None,blank=True)
deleted_flag = models.BooleanField(default=False)
library = models.CharField(null=True,max_length=190,blank=True)
mc = models.CharField(max_length=64)
producer = models.CharField(max_length=64)
editor = models.CharField(max_length=64)
remarks = models.TextField(null=True,blank=True)
audit_time = models.DateTimeField(auto_now=True)
audit_user = models.CharField(null=True,max_length=32)
class Segment(models.Model):
class Meta:
ordering = ['sequence_number']
program = models.ForeignKey(Program,
on_delete=models.CASCADE,
related_name='segments', # link to Program
)
sequence_number = models.DecimalField(decimal_places=2,max_digits=6,default="0.00")
title = models.CharField(max_length=190, blank=True)
bridge_flag = models.BooleanField(default=False)
seg_length_time = models.TimeField()
seg_length_time_delta = models.DurationField(default=timedelta)
seg_run_time = models.TimeField(default="00:00:00",blank=True)
seg_run_time_delta = models.DurationField(default=timedelta)
seg_remaining_time = models.TimeField(default="00:00:00",blank=True)
seg_remaining_time_delta = models.DurationField(default=timedelta)
author = models.CharField(max_length=64,null=True,default=None,blank=True)
voice = models.CharField(max_length=64,null=True,default=None,blank=True)
library = models.CharField(max_length=190)
summary = models.TextField()
audit_time = models.DateTimeField(auto_now=True)
audit_user = models.CharField(null=True,max_length=32)
I also get a error when I attempt to run the server with the Mysql db settings. It will not allow me to add a record into the model and gives me an error complaining about program_id. I dont have anything named program_id that I am aware of.. (I looks through all the code).
It works fine when I run with Sqlite db settings. Adds programs and segments just fine.
Don't know what's wrong here. Any idea what I am doing wrong?
I figured out that my mysql database was not migrating correctly. Part of the problem was it was trying to do all the migrations, but since it was the inital build of the new database I did not need to have all the migrations (and somehow something was happening correctly in there). Removed all of the migrations, did initial makemigrations and migrate, and my Foreign Key was created correctly, no more problems.

Dialect-specific table and column names in sqlalchemy while reusing an existing ORM mapping

I use sqlalchemy's ORM library to interface with one of our application's databases. On one host, the database backend of an application instance is PostgreSQL, while on another, we use MySQL for historical reasons.
The schemas (and relationships) are identical on both backends, but the table and column names in PostgreSQL are lowercase, while on MySQL they are uppercase. Is there any way I can adapt my schema in sqlalchemy such that I don't need to duplicate code?
The following is an example of my schema. Hint, the application is a Confluence wiki ;)
from sqlalchemy import (
BigInteger,
Column,
DateTime,
String,
)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Space(Base):
__tablename__ = 'spaces'
spaceid = Column(BigInteger, primary_key=True)
spacename = Column(String(255))
spacekey = Column(String(255), nullable=False, unique=True)
lowerspacekey = Column(String(255), nullable=False, index=True)
spacedescid = Column(BigInteger, index=True)
homepage = Column(BigInteger, index=True)
creator = Column(String(255), index=True)
creationdate = Column(DateTime, index=True)
lastmodifier = Column(String(255), index=True)
lastmoddate = Column(DateTime)
spacetype = Column(String(255))
spacestatus = Column(String(255), index=True)
Thanks to #rfkortekaas' suggestion, I managed to solve the issue by automapping the schema and subsequently overriding the naming scheme for table and column names.
The documentation of sqlalchemy has two very useful pages:
Overriding Naming Schemes
Intercepting Column Definitions
For posterity's sake:
from sqlalchemy import event
from sqlalchemy.ext.automap import automap_base
from .utilities import to_pascal_case, to_snake_case
AutomapBase = automap_base()
#event.listens_for(AutomapBase.metadata, "column_reflect")
def column_reflect(inspector, table, column_info):
column_info['key'] = to_snake_case(column_info['name'])
def classname_for_table(base, table_name, table):
return to_pascal_case(table_name)
if __name__ == '__main__':
dbcs = 'postgresql://username:password#127.0.0.1:3306/database'
automap_engine = create_engine(dbcs, future=True, echo=verbose > 2)
AutomapBase.prepare(
autoload_with=automap_engine,
classname_for_table=classname_for_table,
)
# Access the automapped classes with AutomapBase.classes.<class_name>

Can't create MySQL Boolean Field Using flask-sqlalchemy & flask-migrate

I have a working flask app with few models. The User model is as follow...
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(128), index=True, unique=True)
password_hash = db.Column(db.String(128))
first_name = db.Column(db.String(32))
last_name = db.Column(db.String(32))
bio = db.Column(db.String(255))
patterns = db.relationship('Pattern', backref='user', lazy='dynamic')
Now I want to add a new boolean column to the model. I'm utilizing MySQL as a database. I have tried the following...
invited = db.Column(db.Boolean, default=0)
but when I runt flask db migrate I get the following...
INFO [alembic.runtime.migration] Context impl MySQLImpl.
I also tried
from sqlalchemy import BOOLEAN # also with Boolean
...
invited = db.Column(BOOLEAN, default=0) # also with Boolean
but get the same error. Reading MySQL documentation found out that MySQL doesn't have boolean type rather TINYINT. But reading this Github thread I understand that the Boolean class will turn into TINYINT based on the dialect. So I did the following...
from sqlalchemy.dialect.mysql import BOOLEAN
and still I get the same error when flask run migrate. Seems like Alembic can't see the changes in the model.
Is there a way to create a boolean field in mysql utilizing flask-migrate and flask-sqlalchemy?
Try below, I'm assuming Flask migrate is not recognizing db.Column(BOOLEAN, default=0).
a_boolean_field = db.Column(db.Boolean(), default=False)
I've just tested it and above works and Flask-Migrate was able to detect it.
You can do it like so
from sqlalchemy import Boolean, Column
invited = Column(Boolean, nullable=False, default=False)
this should work with MySQL.

sqlalchemy multiple databases with same table names not working

I have two databases that I'm working with in with Python using SQLAlchemy, the databases share table names and therefore I'm getting an error message when running the code.
The error message is :
sqlalchemy.exc.InvalidRequestError: Table 'wo' is already defined for this MetaData instance. Specify 'extend_existing=True' to redefine options and columns on an existing Table object.
The simplified code is below:
from sqlalchemy import create_engine, Column, Integer, String, DateTime, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship, backref
from mysql.connector.connection import MySQLConnection
Base = declarative_base()
def get_characterset_info(self):
return self.get_charset()
MySQLConnection.get_characterset_info = MySQLConnection.get_charset
mysqlengine = create_engine('mysql+mysqlconnector://......../mp2', echo=True)
MYSQLSession = sessionmaker(bind=mysqlengine)
mysqlsession= MYSQLSession()
MP2engine = create_engine('mssql+pyodbc://......../mp2', echo=True)
MP2Session = sessionmaker(bind=MP2engine)
mp2session= MP2Session()
class MYSQLWo(Base):
__tablename__= 'wo'
wonum = Column(String, primary_key=True)
taskdesc = Column(String)
comments = relationship("MYSQLWocom", order_by="MYSQLWocom.wonum", backref='wo')
class MYSQLWocom (Base):
__tablename__='wocom'
wonum = Column(String, ForeignKey('wo.wonum'), primary_key=True)
comments = Column(String, primary_key=True)
class MP2Wo(Base):
__tablename__= 'wo'
wonum = Column(String, primary_key=True)
taskdesc = Column(String)
comments = relationship("MP2Wocom", order_by="MP2Wocom.wonum", backref='wo')
class MP2Wocom (Base):
__tablename__='woc'
wonum = Column(String, ForeignKey('wo.wonum'), primary_key=True)
location = Column(String)
sublocation1 = Column(String)
texts = Column(String, primary_key=True)
How do I deal with databases having the same table structure? I'm guessing it has something to do with the MetaData instance, but the SQLAlchemy documentation gets a little confusing when talking about the difference in the class declarative and classical usage..
Since in reality the tables had different structures, the solution was to simply create a separate declarative base. If the tables indeed had the same structure, I would have only needed one class for both tables.
Base = declarative_base()
Base2 = declarative_base() #this is all I needed
class MYSQLWo(Base):
....
class MYSQLWocom(Base):
....
class MP2Wo(Base2):
....
class MP2Wocom(Base2)
http://groups.google.com/group/sqlalchemy/browse_thread/thread/afe09d6387a4dc69?hl=en
You can use one db instance with two Model to bypass this problem.
And this can also be used to implement a master/slave use case in Flask-SQLAlchemy.
Just like this:
app = Flask(__name__)
app.config['SQLALCHEMY_BINDS'] = {'rw': 'rw', 'r': 'r'}
db = SQLAlchemy(app)
db.Model_RW = db.make_declarative_base()
class A(db.Model):
__tablename__ = 'common'
class B(db.Model_RW):
__tablename__ = 'common'