I'm using SQLAlchemy and Pycharm, but PyCharm can't see methods of SQLAlchemy for autocomplete function.
Code:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.sqlite3'
db = SQLAlchemy(app)
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(16), index=True, unique=True)
if __name__ == '__main__':
db.create_all()
app.run(debug=True)
For example, if I want call SQLAlchemy method of User class i must type full method name manually User.query.filter_by(username='peter').first()
Autocomplete example
How to make autocomplete work for SQLAlchemy?
1) Yes, project was reloaded several times
2) Yes, right interpreter in File | settings | Project
3) Yes, PyCharm is not in Power Save Mode
4) Yes, I have Professional edition.
PyCharm (or any other IDE) can't find most methods (query(), add(), commit() etc) as they are not defined in flask_sqlalchemy.SQLAlchemy class. SQLAlchemy methods are added to flask_sqlalchemy.SQLAlchemy objects dynamically during initialization. You can find this initialization function in flask_sqlalchemy module:
def _include_sqlalchemy(obj):
for module in sqlalchemy, sqlalchemy.orm:
for key in module.__all__:
if not hasattr(obj, key):
setattr(obj, key, getattr(module, key))
Just for the testing: you can type from sqlalchemy.orm import Query. I think PyCharm will find it's objects just fine.
I can't suggest any solution, maybe someone else here know about possibility of dynamically added attributes autocompletion.
class User(db.Model):
query: db.Query # Type hint here
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(16), index=True, unique=True)
adding type hint query: db.Query
Related
I started a simple Flask project, i'm using SLQAlchemy to handle my Database. My problem is that every time i run my app, i'll get the following error:
File "C:\Users\user\AppData\Local\Programs\Python\Python38\lib\site-packages\flask_sqlalchemy\__init__.py", line 137, in __init__
track_modifications = app.config['SQLALCHEMY_TRACK_MODIFICATIONS']
KeyError: 'SQLALCHEMY_TRACK_MODIFICATIONS'
Here is my code:
from flask import Flask, request, jsonify
import json
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import Column, Integer, String
from sqlalchemy.schema import FetchedValue
app = Flask(__name__)
db = SQLAlchemy()
class User(db.Model):
__tablename__ = 'users'
__table_args__ = {'schema': 'tvtg'}
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(350), nullable=False)
email = db.Column(db.String(350), nullable=False)
password = db.Column(db.String(350), nullable=False)
#app.route('/')
def hello_world():
print('HERE')
peter = User.query.filter_by(name='peter').first()
print(peter)
return 'hello_world'
if __name__ == "__main__":
app.run()
Can anyone help me find what i'm doing wrong? The traceback of the error is not helping me much
Actually, it looks like when using flask-sqlalchemy, you have to set a value for SQLALCHEMY_TRACK_MODIFICATIONS - even the documentation says otherwise (defaults to None).
Whether you want to set it to True or False is up to your use case, see documentation
https://flask-sqlalchemy.palletsprojects.com/en/2.x/config/
Or have a look at this great tutorial
https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-iv-database
The unreleased Flask-Sqlalchemy version 3 sets a new default of False.
We use a central class model for a wide variety of python modules. This model is defined using SQLAlchemy. The classes all inherit from declarative_base.
For example, our model definitions look something like this:
Base = declarative_base()
class Post(Base):
__tablename__ = 'Posts'
id = Column(INT, primary_key=True, autoincrement=True)
body = Column(TEXT)
timestamp = Column(TIMESTAMP)
user_id = Column(INT, ForeignKey('Users.uid'))
We have been building a flask web-application in which we employ this same model. We have discovered a tricky problem in that flask-sqlalchemy appears to be designed in such a way that it expects all classes used in its model to have been defined by passing in an active instance of the session. Here is an example of a "proper" flask-sqalchemy class model definition:
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(120), unique=True)
Note that the above example for flask-sqlalchemy requires an already-instantiated sql session. This has horrified us, because we are completely at a loss as to how to integrate our SqlAlchemy model into flask. We really want to use the flask-security suite in particular.
This problem has been brought up before on SO. Here, for example:
How to use flask-sqlalchemy with existing sqlalchemy model?
Our requirements are different from those of whoever accepted the response there. The response points out that one loses the ability to use User.query, but this is precisely one of the things we must retain.
It is not feasible to abandon our nice, elegant, central class model definition in favor of what flask-sqlalchemy appears to require. Is there any way for us to associate our model with the SQLAlchemy() object? Bonus points for getting us the .query() method on our classes which appears to be required by flask-security.
Solution:
As of today, the best way to do this is as follows:
Implement or import sqlalchemy base
from sqlalchemy.ext.declarative import declarative_base
base = declarative_base()
class Base(base):
__abstract__ = True
uid = Column(Integer, primary_key=True, autoincrement=True)
Register the external base:
from flask_sqlalchemy import SQLAlchemy
from model.base import Base
app = Flask(__name__)
db = SQLAlchemy(app, model_class=Base)
Archived for posterity:
I spent a lot of time looking for an answer to this. This is a lot easier to do today than it was when I originally asked the question, but it still isn't exactly simple.
For anyone who decides to do security themselves, I recommend the following excellent exposition of common design patterns which use flask, but which avoid employing unnecessary dependencies like flask-security:
https://exploreflask.com/users.html
UPDATE:
For anyone interested, a patch has been in the works for some time related to this. As of now it still isn't released, but you can check its progress here:
https://github.com/mitsuhiko/flask-sqlalchemy/pull/250#issuecomment-77504337
UPDATE:
I have taken the code from the above mentioned patch and created a local override for the SQLAlchemy object which allows one to register an external base. I think this is the best option available until such time as FSA gets around to adding this officially. Here is the code from that class for anyone interested. Tested working with Flask-SqlAlchemy 2.2
Patching in register_external_base:
import flask_sqlalchemy
'''Created by Isaac Martin 2017. Licensed insofar as it can be according to the standard terms of the MIT license: https://en.wikipedia.org/wiki/MIT_License. The author accepts no liability for consequences resulting from the use of this software. '''
class SQLAlchemy(flask_sqlalchemy.SQLAlchemy):
def __init__(self, app=None, use_native_unicode=True, session_options=None,
metadata=None, query_class=flask_sqlalchemy.BaseQuery, model_class=flask_sqlalchemy.Model):
self.use_native_unicode = use_native_unicode
self.Query = query_class
self.session = self.create_scoped_session(session_options)
self.Model = self.make_declarative_base(model_class, metadata)
self._engine_lock = flask_sqlalchemy.Lock()
self.app = app
flask_sqlalchemy._include_sqlalchemy(self, query_class)
self.external_bases = []
if app is not None:
self.init_app(app)
def get_tables_for_bind(self, bind=None):
"""Returns a list of all tables relevant for a bind."""
result = []
for Base in self.bases:
for table in flask_sqlalchemy.itervalues(Base.metadata.tables):
if table.info.get('bind_key') == bind:
result.append(table)
return result
def get_binds(self, app=None):
"""Returns a dictionary with a table->engine mapping.
This is suitable for use of sessionmaker(binds=db.get_binds(app)).
"""
app = self.get_app(app)
binds = [None] + list(app.config.get('SQLALCHEMY_BINDS') or ())
retval = {}
for bind in binds:
engine = self.get_engine(app, bind)
tables = self.get_tables_for_bind(bind)
retval.update(dict((table, engine) for table in tables))
return retval
#property
def bases(self):
return [self.Model] + self.external_bases
def register_base(self, Base):
"""Register an external raw SQLAlchemy declarative base.
Allows usage of the base with our session management and
adds convenience query property using self.Query by default."""
self.external_bases.append(Base)
for c in Base._decl_class_registry.values():
if isinstance(c, type):
if not hasattr(c, 'query') and not hasattr(c, 'query_class'):
c.query_class = self.Query
if not hasattr(c, 'query'):
c.query = flask_sqlalchemy._QueryProperty(self)
# for name in dir(c):
# attr = getattr(c, name)
# if type(attr) == orm.attributes.InstrumentedAttribute:
# if hasattr(attr.prop, 'query_class'):
# attr.prop.query_class = self.Query
# if hasattr(c , 'rel_dynamic'):
# c.rel_dynamic.prop.query_class = self.Query
To be used like so:
app = Flask(__name__)
db = SQLAlchemy(app)
db.register_base(base)
Here is sqlamp docs.
I don't understand how I can connect sqlamp with predefined flask-sqlalchemy session. Docs says something like
from history_meta import VersionedMeta, VersionedListener
app = Flask(__name__)
db = SQLAlchemy(app, session_extensions=[VersionedListener()])
class User(db.Model):
__metaclass__ = VersionedMeta
username = db.Column(db.String(80), unique=True)
pw_hash = db.Column(db.String(80))
but there is no session_extensions in the latest version of flask-sqlalchemy. Maybe I've got to use a session_options, but it is unclear how to use it.
Slightly different syntax now:
db = SQLAlchemy(session_options={'extension': [VersionExtension()]})
I'm following the Flask-SQLAlchemy tutorial.
I have Flask 0.9, sqlalchemy 0.7.8 and flask-sqlalchemy 0.16 on python 2.6.
I'm trying to create a "one to many" relationship, like in their tutorial.
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
addresses = db.relationship('Address', backref='person',
lazy='dynamic')
class Address(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(50))
person_id = db.Column(db.Integer, db.ForeignKey('person.id'))
After that, I can create the database :
from DataBase.Tables.MyClass import db
db.create_all()
It works well when both classes are created on the same file.
It does not work anymore when I want to create this through 2 different files (2 different modules).
This is just an example (I'm trying to do something much more complicated with plenty of classes and I need the relationship to exist between 2 different modules but I'll simplify it so my question can be easier to understand.)
I have 2 modules : Person and Address, both of them have :
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///C:\\MyBase\\Base.sqlite'
db = SQLAlchemy(app)
and each of them has the declaration of the class written before.
My main function is in a 3rd module:
from DataBase.Tables.Person import db as person_db
from DataBase.Tables.Address import db as address_db
if __name__ == "__main__":
import DataBase.Tables.Person
import DataBase.Tables.Address
person_db.create_all()
address_db.create_all()
I still get an error in Eclipse:
*sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'Address.person_id' could not find table 'person' with which to generate a foreign key to target column 'sid'*
I could find another post with someone suggesting the use of "metadata" but I couldn't find a proper way to use that.
Does anyone have an idea to solve this ?
Thanks !
You need to have only one set of the below, and not a separate copy for each model:
app = Flask(my_app_name)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///C:\\MyBase\\Base.sqlite'
db = SQLAlchemy(app)
This can be defined in a separate module (lets call it shared), and imported into each model definition file.
In this case the main module will look more like:
from DataBase.Tables.shared import db
if __name__ == "__main__":
import DataBase.Tables.Person # will load Person model into the db
import DataBase.Tables.Address # will load Address model into the db
db.create_all() # will create all models
I have problem with setting up database connection. I want to set connection, where I can see this connection in all my controllers.
Now I use something like this in my controller:
db = create_engine('mysql://root:password#localhost/python')
metadata = MetaData(db)
email_list = Table('email',metadata,autoload=True)
In development.ini I have:
sqlalchemy.url = mysql://root#password#localhost/python
sqlalchemy.pool_recycle = 3600
How do I set _____init_____.py?
I hope you got pylons working; for anyone else that may later read question I'll present some pointers in the right direction.
First of all, you are only creating a engine and a metadata object. While you can use the engine to create connections directly you would almost always use a Session to manage querying and updating your database.
Pylons automatically setups this for you by creating a engine from your configuration file, then passing it to yourproject.model.__init__.py:init_model() which binds it to a scoped_session object.
This scoped_session object is available from yourproject.model.meta and is the object you would use to query your database. For example:
record = meta.Session.query(model.MyTable).filter(id=42)
Because it is a scoped_session it automatically creates a Session object and associates it with the current thread if it doesn't already exists. Scoped_session passes all action (.query(), .add(), .delete()) down into the real Session object and thus allows you a simple way to interact the database with having to manage the non-thread-safe Session object explicitly.
The scoped_session, Session, object from yourproject.model.meta is automatically associated with a metadata object created as either yourproject.model.meta:metadata (in pylons 0.9.7 and below) or yourproject.model.meta:Base.metadata (in pylons 1.0). Use this metadata object to define your tables. As you can see in newer versions of pylons a metadata is associated with a declarative_base() object named Base, which allows you to use SqlAlchemy's declarative style.
Using this from the controller
from yourproject import model
from yourproject.model import Session
class MyController(..):
def resource(self):
result = Session.query(model.email_list).\
filter(model.email_list.c.id=42).one()
return str(result)
Use real connections
If you really want to get a connection object simply use
from yourproject.model import Session
connection = Session.connection()
result = connection.execute("select 3+4;")
// more connection executions
Session.commit()
However this is all good, but what you should be doing is...
This leaves out that you are not really using SqlAlchemy much. The power of SqlAlchemy really shines when you start mapping your database tables to python classes. So anyone looking into using pylons with a database should take a serious look at what you can do with SqlAlchemy. If SqlAlchemy starts out intimidating simply start out with using its declarative approach, which should be enough for almost all pylons apps.
In your model instead of defining Table constructs, do this:
from sqlalchemy import Column, Integer, Unicode, ForeignKey
from sqlalchemy.orm import relation
from yourproject.model.meta import Base
class User(Base):
__tablename__ = 'users'
# primary_key implies nullable=False
id = Column(Integer, primary_key=True, index=True)
# nullable defaults to True
name = Column(Unicode, nullable=False)
notes = relation("UserNote", backref="user")
query = Session.query_property()
class UserNote(Base):
__tablename__ = 'usernotess'
# primary_key implies nullable=False
id = Column(Integer, primary_key=True, index=True)
userid = Column(Integer, index=True, ForeignKey("User.id"))
# nullable defaults to True
text = Column(Unicode, nullable=False)
query = Session.query_property()
Note the query objects. These are smart object that live on the class and associates your classes with the scoped_session(), Session. This allows you to event more easily extract data from your database.
from sqlalchemy.orm import eagerload
def resource(self):
user = User.query.filter(User.id==42).options(eagerload("notes")).one()
return "\n".join([ x.text for x in user.notes ])
1.0 version of Pylons use declarative syntax. More about this, you can see here .
In mode/init.py you can write somthing like this:
from your_programm.model.meta import Session, Base
from sqlalchemy import *
from sqlalchemy.types import *
def init_model(engine):
Session.configure(bind=engine)
class Foo(Base) :
__tablename__ = "foo"
id = Column(Integer, primary_key=True)
name = Column(String)
...
What you want to do is modify the Globals class in your app_globals.py file to include a .engine (or whatever) attribute. Then, in your controllers, you use from pylons import app_globals and app_globals.engine to access the engine (or metadata, session, scoped_session, etc...).