I have an app currently working fine Automapping one database, but I need to access another database now as well. I tried to follow the Flask-SQLAlchemy documentation here: http://flask-sqlalchemy.pocoo.org/2.1/binds/, but it doesn't seem to work with the automap_base.
The only changes I made were creating SQLALCHEMY_BINDS and adding the __bind_key__ in models.py. The error I get is
sqlalchemy.exc.ArgumentError: Mapper Mapper|Table2|table2 could not assemble any primary key columns for mapped table 'table2'
However, both tables have a primary key column, and if I get rid of SQLALCHEMY_BINDS, set the URI to that of db2, and only have table2 in models.py, everything works fine.
I'm clearly doing something wrong, but I have no idea what it is. It looks like Flask is still looking for table2 in db1. I think my problem is that some change needs to be made to __init__.py as well, but I don't know what that would be.
config.py
SQLALCHEMY_DATABASE_URI = 'mysql://user#host/db1'
SQLALCHEMY_BINDS = {
'otherDB': 'mysql://user#host/db2',
}
__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.ext.automap import automap_base
app = Flask(__name__)
app.config.from_object('config')
db = SQLAlchemy(app)
db.Model = automap_base(db.Model)
from app import models
db.Model.prepare(db.engine, reflect=True)
models.py
class Table1(db.Model):
__tablename__ = 'table1'
class Table2(db.Model):
__bind_key__ = 'otherDB'
__tablename__ = 'table2'
Automap is a extension of sqlalchemy to reflect an existing database into a new model. It has not been baked into flask-sqlalchemy. Plz see the issue here. You can connect to multiple databases with Flask-SQLAlchemy like this:
__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config.from_object('config')
db = SQLAlchemy(app)
from app import models
if __name__ == "__main__":
# show the table schema
print m3.Table1.__table__.c
print m3.Table2.__table__.c
models.py
db.reflect() # reflection to get table meta
class Table1(db.Model):
__tablename__ = 'table1'
class Table2(db.Model):
__tablename__ = 'table2'
__bind_key__ = 'otherDB'
Related
I am using the SQLModel library to do a simple select() like described on their official website. However I am getting Column expression or FROM clause expected error message
from typing import Optional
from sqlmodel import Field, Session, SQLModel, create_engine, select
from models import Hero
sqrl = f"mysql+pymysql:///roo#asdf:localhost:3306/datab"
engine = create_engine(sqrl, echo=True)
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
def select_heroes():
with Session(engine) as session:
statement = select(Hero)
results = session.exec(statement)
for hero in results:
print(hero)
def main():
select_heroes()
if __name__ == "__main__":
main()
this is my models/Hero.py code:
from datetime import datetime, date, time
from typing import Optional
from sqlmodel import Field, SQLModel
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
secret_name: str
age: Optional[int] = None
created: datetime
lastseen: time
when I run app.py I get the sqlalchemy.exc.ArgumentError: Column expression or FROM clause expected, got <module 'models.Hero' from '/Users/dev/test/models/Hero.py'>. message
The error message <Column expression or FROM clause expected, got module 'models.Hero' from '/Users/dev/test/models/Hero.py'> tells us:
SQLModel / SQLAlchemy unexpectedly received a module object named models.Hero
that you have a module named Hero.py
The import statement from models import Hero only imports the module Hero. Either
change the import to import the model*
from models.Hero import Hero
change the code in select_heroes to reference the model†
statement = select(Hero.Hero)
* It's conventional to use all lowercase for module names; following this convention will help you distinguish between modules and models.
† This approach is preferable in my opinion: accessing the object via the module namespace eliminates the possibility of name collisions (of course it can be combined with lowercase module names).
Suppose I have a simple SQLAlchemy class and a simple Flask o FastAPI implementation like this:
from sqlalchemy.ext.declarative import declarative_base
from pydantic import BaseModel
Base = declarative_base()
class A(Base):
__tablename__ = 'as'
my_id = Column(String)
class AModel(BaseModel):
myId:str = None
And a simple endpoint like this:
#app_router.get('/a')
def get_all_a(session:Session = Depends(get_session)):
return session.query(A).all()
How could I ensure that the returned list of this endpoint yields in camelCase like this:
[{'myId': 'id1'},{'myId': 'id2'}, ...]
Note: My application is rather complex, as I also have pagination implemented and some post-processings require a little bit more that just snake_case to camelCase conversion, so the simplest solution would be the best.
I've tried overriding dict() methods and similar stuff with no luck, simply cannot understand how FastAPI processes the results to obtain a JSON.
require a little bit more that just snake_case to camelCase conversion
Well, if you don't use response_model you don't have a lot of choices.
The solution is returning your dict with a conversion from snake_case to camelCase. You have functions that do it recursively.
Why it is the best solution ?
using regex it's super fast, faster than any lib that convert dict to obj like pydantic
If you definitely don't want to do this, well your only solution is using pydantic models, attrs or dataclasses and convert your db query output to one of those models type with camelCase variable name (dirty).
Since you are using fastapi you should use all the power of it.
I would suggest this:
from typing import List
from sqlalchemy.ext.declarative import declarative_base
from pydantic import BaseModel, Field
from pydantic import parse_obj_as
Base = declarative_base()
class A(Base):
__tablename__ = 'as'
my_id = Column(String)
class AModel(BaseModel):
myId: str = Field(alias="my_id", default=None)
#app_router.get('/a', response_model=List[AModel])
def get_all_a(session:Session = Depends(get_session)):
return parse_obj_as(List[AModel], session.query(A).all())
Keep in mind that having classes variables in CamelCase is not a good practice.
The gold answer would be to not return camel but snake and let your client do the work of conversion if needed.
I'm new to python. I am trying to create a connection string to a mysql, but from an input via a webform.
My first thought was to create a function to call to pass in the details. Here is what I have:
from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
import pymysql
app = Flask(__name__)
def db_connection(user, password, ep, db):
connection = f"mysql+pymysql://{user}:{password}#{ep}/{db}"
app.config['SQLALCHEMY_DATABASE_URI'] = connection
app.config['SQLALCHEMY_TRACK_MODIFICATION'] = False
db = SQLAlchemy(app)
class Lab(db.Model):
id = db.Column(db.Integer, primary_key=True)
store = db.Column(db.String(80), unique=True, nullable=False)
item = db.Column(db.String(120), unique=True, nullable=False)
quantity = db.Column(db.Integer, nullable=False)
db.create_all()
The details of the "user", "password", "ep" (endpoint/hostname), "db" for the db_connection function are passed in via filling out a webform.
I think i might be going about this the wrong way. The end result that I want is for my user to go to the webform, fill the details about the DB to connect to, click submit then the function(or what ever) would establish the connection with those details.
The form I have passes to an app.route to establish to call the above function
#app.route('/save_settings', methods=["GET", "POST"])
def save_settings():
ep_data = request.form['endpoint']
db_data = request.form['database']
user_data = request.form['username']
password_data = request.form['password']
db_connection(user=user_data, password=password_data, db=db_data, ep=ep_data)
When I try it, I get the following error:
AssertionError: A setup function was called after the first request was handled. This usually indicates a bug in the application where a module was not imported and decorators or other functionality was called too late.
To fix this make sure to import all your view modules, database models and everything related at a central place before the application starts serving requests.
Ok, after some playing around I seemed to have fixed it. I hope this helps others that were stuck on this too.
The mistakes I made:
the class does not come under the function
before the class needs to have: db = SQLAlchemy(app)
Here is my modified code:
from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
import pymysql
app = Flask(__name__)
db = SQLAlchemy(app)
def db_connection(user, password, ep, db):
connection = f"mysql+pymysql://{user}:{password}#{ep}/{db}"
app.config['SQLALCHEMY_DATABASE_URI'] = connection
app.config['SQLALCHEMY_TRACK_MODIFICATION'] = False
class Lab(db.Model):
id = db.Column(db.Integer, primary_key=True)
store = db.Column(db.String(80), unique=True, nullable=False)
item = db.Column(db.String(120), unique=True, nullable=False)
quantity = db.Column(db.Integer, nullable=False)
I have multiple (not only junit) consumer applications, which create pacts in pact broker.
These consumers have many different git branches - e.g.:
consumer1: release123, release007, release999
consumer2: release69, release420, release50
Question
how to run provider tests against specific combinations of consumers and their tags?
e.g. consumer1 release007, consumer2 release69 AND release50 ?
is these something like -Dpactbroker.consumers=consumer1:release007,consumer2:release69,consumer2:release50 ?
I especially need this for junit, as we mostly use java apps as providers.
What I have found:
in junit, there is annotation #PactBroker which allows you to specify tags and consumers
according to description, these can be set via system properties pactbroker.tags and pactbroker.consumers
there can be specified multiple of each, separated by comma - e.g.: -Dpactbroker.consumers=consumer1,consumer2
I havent found if tags and consumers can be paired while running provider tests
Please try below solution might be it will help
You can add like below on Provider Class
API used
import au.com.dius.pact.provider.junit.IgnoreNoPactsToVerify;
import au.com.dius.pact.provider.junit.Provider;
import au.com.dius.pact.provider.junit.State;
import au.com.dius.pact.provider.junit.loader.PactBroker;
import au.com.dius.pact.provider.junit.loader.PactBrokerAuth;
import au.com.dius.pact.provider.junit.loader.PactFolder;
import au.com.dius.pact.provider.junit.target.Target;
import au.com.dius.pact.provider.junit.target.TestTarget;
import au.com.dius.pact.provider.spring.SpringRestPactRunner;
import au.com.dius.pact.provider.spring.target.SpringBootHttpTarget;
Provider Added on Class
#IgnoreNoPactsToVerify
#PactBroker(
authentication = #PactBrokerAuth(password = "*******", username = "********"),
host = "*******",
tags = {"test", "dev", "latest"},
port = "*****",
failIfNoPactsFound = false,
protocol = "http")
On consumer end you can add on each test case like below
API used
import au.com.dius.pact.consumer.Pact;
import au.com.dius.pact.consumer.PactProviderRuleMk2;
import au.com.dius.pact.consumer.PactVerification;
import au.com.dius.pact.consumer.dsl.PactDslWithProvider;
import au.com.dius.pact.model.RequestResponsePact;
Test Case Level
#Pact(consumer = "consumer-service-name")
I am attempting to use WTForms with the SQLAlchemy extension on a Pyramid application.
I have done:
from wtforms import Form, TextField,TextAreaField, validators
from wtforms.ext.sqlalchemy.fields import QuerySelectField
from app.models import DBSession
from app.models import ParentModel
class NewChild(Form):
title = TextField('Title:', [validators.Required()])
intro = TextAreaField('Introduction:')
body = TextAreaField('Body:')
parent = QuerySelectField(query_factory=DBSession().query(ParentModel).all)
DBSession is defined as
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
The query seems to work, but the display in my template is reading
<app.models.ParentModel object at 0x9xxx>
or some such. What am I doing wrong?
You need to define a a __str__ method on ParentModel