Import just in root file but not in helper files Python - python

I am building a Flask API with a SQLAlchemy database. In my app.py file (the main file) I have all the necessary imports and setups, like so:
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///data.db"
db = SQLAlchemy(app)
Th problem is, I would like to move this model
class User(db.Model):
id = db.Column(db.integer, primary_key=True)
username = db.Column(db.String(20))
password = db.Column(db.String(16))
to a separate file, to keep everything neat, but it inherits from the import, and from the
app = Flask(__name__)
, but I cant have two of those, because the app crashes. In javascript for example, it is ppossible to declare a function that requires certain imports without importing them in that same file, as long as that function gets exported to another file where these requirements are met. In python though, it throws a db is not defined error. Any idea how to fix this?

You did not mention to the table name in User model. It should be
class User(db.Model):
__tablename__ = 'user_table_name'
id = db.Column(db.integer, primary_key=True)
username = db.Column(db.String(20))
password = db.Column(db.String(16))
replace user_table_name with the actual name of the table in your SQLite database.

Related

Sql alchemy sqlalchemy.exc.NoReferencedTableError not creating tables

I'm working with flask and trying to learn sqlalchemy, I have a database where I added a third table to make a new feature, but the entire project stopped working, it says:
sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'url_reference.folder_rel' could not find table 'reffolders' with which to generate a foreign key to target column 'id'
this is the dbs.py
from . import db
from flask_login import UserMixin
from sqlalchemy.sql import func
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(200), unique=True)
user_password = db.Column(db.String(150))
user_name = db.Column(db.String(400))
user_refs = db.relationship("RefFolders")
class RefFolders(db.Model):
id = db.Column(db.Integer, primary_key=True)
folder_title = db.Column(db.String(1000))
date_added = db.Column(db.DateTime(timezone=True), default=func.now())
user_rel = db.Column(db.Integer, db.ForeignKey("user.id"))
url_rel = db.relationship("UrlReference")
class UrlReference(db.Model):
id = db.Column(db.Integer, primary_key=True)
ref_title = db.Column(db.String(1000))
ref_url = db.Column(db.String(30000))
date_added = db.Column(db.DateTime(timezone=True), default=func.now())
folder_rel = db.Column(db.Integer, db.ForeignKey("reffolders.id"))
this is where I make the database in the __init__.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from os import path
from flask_login import LoginManager
db = SQLAlchemy()
DB_NAME = "database.db"
def app():
app = Flask(__name__)
app.config['SECRET_KEY'] = '<here is the key in my code>'
app.config['SQLALCHEMY_DATABASE_URI'] = f"sqlite:///{DB_NAME}"
db.init_app(app)
# making the blueprints usable
from .views import views
from .auth import auth
app.register_blueprint(views, url_prefix="/")
app.register_blueprint(auth, url_prefix="/")
from .dbs import User
from .dbs import UrlReference
from .dbs import RefFolders
db_create(app)
# Login manager
login_manager= LoginManager()
login_manager.login_view = 'views.home'
login_manager.init_app(app)
# Searching and loading user
#login_manager.user_loader
def load_user(id):
return User.query.get(int(id))
return app
def db_create(app):
if not path.exists("project/" + DB_NAME):
db.create_all(app=app)
I don't know what happened, maybe a problem with having a double foreign key?
I think flask or its extension guesses the table name you want, like CamelCase --> camel_case.
So in this case the tablename is probably ref_folders. You might want to just use tablename to set the table name yourself although I think their default is a good convention.
As a side note I would recommend using _id for your ids otherwise things might get pretty confusing between object relations and actual ids, ie. use user_id instead of user_rel in RefFolders.
SEE:
https://flask-sqlalchemy.palletsprojects.com/en/2.x/models/#declaring-models
Some parts that are required in SQLAlchemy are optional in
Flask-SQLAlchemy. For instance the table name is automatically set for
you unless overridden. It’s derived from the class name converted to
lowercase and with “CamelCase” converted to “camel_case”. To override
the table name, set the __tablename__ class attribute.

How to add table to database?

I am trying to use this code to add a table to a database in a Flask app on localhost - but it does not work. What should I do?
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI']='postgresql://postgres:123#localhost:5432/postgres'
db = SQLAlchemy(app)
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(), nullable=False)
db.create_all()
I changed my app name from "flask-hello-app" to "app"
and typed these 3 commands in terminal:
python
from app import db
db.create_all()
and it worked for me.
db.session.commit()
Good luck.
I just try your code using both Postgres and MySQL
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://root:55665566#localhost:5432/test'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
# from yourapplication import db
db.create_all()
Since I have created the table successfully,
I suggest you check some details below
Check the role attribute for the account.
Check the connection to the database, using command \du to confirm your accounts info, suppose you need to create a user 'postgres' which should show up in the table above.

Flask SQLAlchemy throws "NoReferencedTableError" when arranging codes in package structure

I am learning flask and trying to arrange my codes in the "package structure" according to some online tutorials. I have put together some minimal codes to replicate my problem.
First the tree structure of my codes is
.
└── test001
├── __init__.py
├── models.py
└── views.py
The source codes for each file:
"__init__.py":
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SECRET_KEY'] = 'somerandomnumber'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)
from test001 import views
Source for "models.py":
from test001 import db,app
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False)
favoritejob_id = db.Column(db.Integer, db.ForeignKey('favoritejob.id'), nullable=True)
favoritejob = db.relationship("FavoriteJob", backref=db.backref("users"), lazy=True)
class FavoriteJob(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20), nullable=False)
def __repr__(self):
return f"<FavoriteJob ID={self.id} Name={self.name}>"
Source for "views.py" (putting all routes together):
from test001.models import User, FavoriteJob
# #app.route("/register", methods=['GET', 'POST'])
# def register():
# PLACE HOLDER FOR USER REGISTRATION
With the codes above, I then go to python3 shell and to the following:
from test001 import db
db.create_all()
and I get the following errors:
---------------------------------------------------------------------------
NoReferencedTableError Traceback (most recent call last)
<ipython-input-2-653042798025> in <module>
----> 1 db.create_all()
...
...
~/.local/lib/python3.6/site-packages/sqlalchemy/sql/schema.py in column(self)
2045 "foreign key to target column '%s'"
2046 % (self.parent, tablekey, colname),
-> 2047 tablekey,
2048 )
2049 elif parenttable.key not in parenttable.metadata:
NoReferencedTableError: Foreign key associated with column 'user.favoritejob_id' could not find table 'favoritejob' with which to generate a foreign key to target column 'id'
I did quite a bit searching about this error but couldn't find anything wrong with models.py. Then I removed the following line from views.py, which makes views.py an empty file
from test001.models import User, FavoriteJob
Then "db.create_all()" starts working.
However I can't leave views.py empty as I will put all the views/url routes in the file and will need to use the User and FavoriteJob classes.
I couldn't figure out why the import line in views.py would cause db.create_all() to fail. I am quite new to flask and sqlalchemy so sorry if there is something obviously wrong with my setup. I appreciate your help.
Flask-SQLAlchemy defines the table names from descriptive base models (unless you've defined one yourself). And if a model name is e.g. CamelCase, it sets the table name as camel_case, not camelcase.
In your model definitions, you have a related field on User model -- favoritejob_id, which is related to FavoriteJob model (as per the relationship declaration favoritejob). As you don't have any explicit table name for the FavoriteJob model, Flask-SQLAlchemy will name it as favorite_job. But your related field definition has:
db.ForeignKey('favoritejob.id')
as the table favoritejob does not exist, you're getting the error (as expected).
You have a couple of options to handle this:
The easiest option probably is to rename the related table name:
db.ForeignKey('favorite_job.id')
Or give the table name for FavoriteJob model as favoritejob:
class FavoriteJob(db.Model):
__tablename__ = 'favoritejob'

lazy=True in (Flask-)SQLAlchemy

I'm learning SQLAlchemy and I want to make sure that I've understood the backref parameter in relationship correctly.
For example
from app import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True)
posts = db.relationship('Post', backref='author', lazy=True)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
Say I have a User object j = models.User.query.get(1). My question is, is there any difference between the following things?
j.posts
Post.query.filter_by(author=j).all()
Post.query.with_parent(j).all()
Post.query.with_parent(j, property='posts').all()
Post.query.with_parent(j, property=User.posts).all()
The results returned are same, but I don't know whether the SQL statements executed are identical.
What I've tried
The SQLAlchemy docs says:
with_parent(instance, property=None, from_entity=None)
...the given property can be None, in which case a search is performed against this Query object’s target mapper.
So the last three statements seem same, but I don't really understand what does this Query object’s target mapper refer to. Is it Post in this case, for this query is performed on Post?
Even if the generated SQL statements are identical, the commands you enlisted may have a different impact on your application, e.g. j.posts will cache (memoize, do not confuse with Werkzeug caching) results you have got, while others will fetch them every single time.
If you remove .all() from your queries you can simply print them:
query = Post.query.filter_by(author=j)
print(query)
Which would result in:
SELECT post.id AS post_id, post.body AS post_body, post.user_id AS post_user_id
FROM post
WHERE ? = post.user_id
Using .all() is essentially like getting [m for m in query]).
The trick with query-printing will not work for j.posts which will return something like:
> print(j.posts)
> [Post(...), Post(..)]
Still, you can see all the silently emitted queries using built-in sqlalchemy loggers. See the following code:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.engine import Engine
from sqlalchemy import event
import logging
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/tests.db'
db = SQLAlchemy(app)
logging.basicConfig()
logger = logging.getLogger('sqlalchemy.engine')
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True)
posts = db.relationship('Post', backref='author', lazy=True)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
db.drop_all()
db.create_all()
user = User(username='test', posts=[Post(body='some body')])
db.session.add(user)
db.session.commit()
# start logging
logger.setLevel(logging.DEBUG)
j = User.query.get(1)
queries = {
"j.posts",
"Post.query.filter_by(author=j)",
"Post.query.with_parent(j)",
"Post.query.with_parent(j, property='posts')",
"Post.query.with_parent(j, property=User.posts)",
}
def test_queries():
for name in queries:
print('\n=======')
print('Executing %s:' % name)
query = eval(name)
print(query)
test_queries() # you should see j.posts query here
print('Second test')
test_queries() # but not here
Getting back to your question: yes, the emitted SQL queries are identical.
In Query object’s target mapper, Query object's target refers to Post in your example. Decoupling this, when you declare Post class, inheriting from db.Model, for SQLAlchemy it is like creating an object Post and mapping the properties of this object to columns of specially created table.
Underneath there is an instance of Mapper class, which is responsible for the mapping for every single model that you create (learn more about mapping here: Types of Mappings). You can simply get this mapper calling class_mapper on your model or object_mapper on an instance of your model:
from sqlalchemy.orm import object_mapper, class_mapper,
from sqlalchemy.orm.mapper import Mapper
assert object_mapper(j) is class_mapper(User)
assert type(class_mapper(User)) is Mapper
The Mapper has all the necessary information about the columns and relations you have in your model. When calling Post.query.with_parent(j) this information is used to find a property (i.e. relationship) relating Post and User objects, so in your case to populate 'property' with User.posts.
To see the queries you can run your python script with -i and then run each query individually and it will print out the SQL code it runs.
Example:
main.py:
import sqlalchemy
from sqlalchemy import create_engine, Column, Integer, String, Sequence
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
import os
engine = create_engine('sqlite:///:memory:', echo=True)
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, Sequence('user_id_seq'), primary_key=True)
name = Column(String(50))
fullname = Column(String(50))
password = Column(String(12))
def __repr__(self):
return "< User(name={}, fullname={}, password={} )>".format(self.name, self.fullname, self.password)
Base.metadata.create_all(engine)
ed_user= User(name='ed', fullname='Ed Jones', password='edpassword')
Session = sessionmaker(bind=engine, autoflush=False)
session = Session()
session.add(ed_user)
session.add_all([
User(name='wendy', fullname='Wendy Williams', password='foobar'),
User(name='mary', fullname='Mary Contraty', password='xxg527'),
User(name='fred', fullname='Fred Flinstone', password='blah')
])
session.commit()
os.system('clear')
Now you run it with python -i main.py, type: session.query(User).filter_by(name='ed').first() and you will see the SQL generated. After running all of your tests I concluded that they are all identical. With this method you can test any query and see if there is any difference.
p.s. I added the os.system('clear') to remove all the unnecessary output from creating the database and some other stuff.

SQLAlchemy create_all without flask

I have a model being used by my flask server as well as a command-line tool. I want both the script and flask app to be able to create tables programmatically using this model.
In the flask app, I do this:
from models import db
db.app = app
db.init_app(app)
db.create_all()
However I can't use this code in the command-line tool as there is no 'app', and without the app I get
RuntimeError: application not registered on db instance and no application bound to current context
Which makes sense. So how do I make it work with the command-line tool without creating another version of my model file?
My model file (roughly):
class User(db.Model):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
account_id = Column(BigInteger, nullable=False)
team_name = Column(String, nullable=False)
external_id = Column(String(36), nullable=False)
# should not be filled with web app, but for misc notes we take later
notes = Column(String)
def __str__(self):
return "User (account_id={}, team_name='{}', external_id='{}')".format(
self.account_id, self.team_name, self.external_id)
__table_args__ = (
UniqueConstraint("account_id", name="unique_account_id"),
UniqueConstraint("external_id", name="unique_external_id"),
UniqueConstraint("team_name", name="unique_team_name")
)

Categories