I am going through a flask/sqlalchemy tutorial
https://pythonhosted.org/Flask-SQLAlchemy/quickstart.html#a-minimal-application
and I configured my database url to be:
postgres://username:password#localhost:5432/dbname
when I run db.create_all() in the interactive python shell it doesn't throw any errors, but it doesn't do anything either.
From what I understand it is supposed to create the User table with three columns; id, username, and email .
from flask import Flask, url_for, render_template
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgres://username:password#localhost:5432/dbname'
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)
def __init__(self, username, email):
self.username = username
self.email = email
def __repr__(self):
return '<User %r>' % self.username
app.debug = True
#app.route("/")
def hello():
return render_template('hello.html')
if __name__ == "__main__":
app.run()
It should be the exact format.
app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql://postgres:postgres#localhost/DBNAME"
Where postgres is username and postgres is password, localhost is address
Try using postgresql in the URL instead of postgres.
That's the form that's used both for JDBC and in the SQLAlchemy doc (http://docs.sqlalchemy.org/en/rel_0_9/dialects/postgresql.html).
Once you install PostgreSQL you can do this:
app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql://<username>:<password>#localhost/<database_name>"
Where:
<username> is your username (by default postgres)
<password> is your password (by default postgres)
<database_name> the name of your database (by deafault postgres if you are using the DB created once you installed PostgreSQL).
If you have created a role (a user) for example admin with password admin and a database named posts then you can do this:
app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql://admin:admin#localhost/posts"
more info here https://flask-sqlalchemy.palletsprojects.com/en/2.x/config/
Related
I'm setting up a new Flask app, where I want to have all of my SQLAlchemy models in separate files. The overall structure should end up looking like this:
flask-app
|
|--api
| |--user.py
| |--example.py
|
|--controllers
| |--user.py
| |--example.py
|
|--models
| |--user.py
| |--example.py
|
|-main.py
|-modelref.py
|-config.py
In terms of code here's what i have so far:
main.py:
import os
from flask import Flask, send_from_directory
from flask_bcrypt import Bcrypt
from flask_cors import CORS
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
import modelref
from apiref import load_api
app = Flask(__name__, static_url_path='', static_folder='./../react/public')
app.config.from_object(os.getenv("APP_SETTINGS", "config.Development"))
CORS(app)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
bcrypt = Bcrypt(app)
#app.route("/", defaults={'path':''})
def serve(path):
return send_from_directory(app.static_folder,'index.html')
load_api(app)
The modelref import is a shorthand of sorts. In the modelref.py file I import every model that I have in the models folder like so:
modelref.py:
from models.user import User
from models.example import Example
Then the model itself is something along the lines of:
models/user.py:
class User(db.Model):
__tablename__ = "user"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(64), unique=True, nullable=False)
email = db.Column(db.String(255), unique=True, nullable=False)
password = db.Column(db.String(255), nullable=False)
first_name = db.Column(db.String(128), nullable=False)
last_name = db.Column(db.String(128), nullable=False)
registered_on = db.Column(db.DateTime, nullable=False)
def __init__(self, username, email, password, first_name, last_name):
self.username = username
self.email = email
self.password = bcrypt.generate_password_hash(password, flask.current_app.config.get('BCRYPT_LOG_ROUNDS')).decode()
self.first_name = first_name
self.last_name = last_name
self.registered_on = datetime.datetime.now()
def save(self):
db.session.add(self)
db.session.commit()
The model gets manipulated in a controller class defined here:
controllers/user.py:
class UserController:
#staticmethod
def register(username: str, password: str, email: str, first_name: str, last_name: str) -> dict:
result: dict = {}
try:
user = User(
username=username,
password=password,
email=email,
first_name=first_name,
last_name=last_name
)
user.save()
except IntegrityError:
User.rollback()
raise ResourceExists("user already exists")
return login(user.username, user.password);
#staticmethod
def login(username: str, password: str):
user = User.query.filter_by(username).first()
if user:
if bcrypt.check_password_hash(user.password, password):
token = user.encode_auth_token(user.id)
return {
"username": user.username,
"first_name": user.first_name,
"last_name": user.last_name,
"token": token
}
else:
abort(400, "Invalid Password")
else:
abort(404, "User Not Found")
The controller is accessed in an API resource defined using pluggable views. The views and routes are registered with the app in the load_app(app) method. I'm not sharing that whole pipeline, because I believe it is out of scope, but if anyone believes it's not, than I will add it into the post.
So now here's the problem. If I try to access the model in the code or run flask db migrate, I get an error that db is not defined.
A solution I've seen is to add a db=SQLAlchemy line in the model, but then the flask db migrate doesn't find this model, likely because the mentioned line creates a new SQLAlchemy instance, so it's essentially unaware of the models existence.
With this setup I'm unable to import the db object from main since that causes a circular import due to the model being imported in the controller.
Is there a way I can pass a reference to db to the model without an import, or make the model search for the "current" instance of 'db'. Or is there any way to reorganize the main code so that I can maintain the general structure, but be able to run migrations?
It turns out that in a case such as mine, the best option is to apply the application factory pattern. I now have an app.py module, where I initialize all the modules I need in a create_app method like so:
app.py:
import os
from flask import Flask
from flask_bcrypt import Bcrypt
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
db = SQLAlchemy()
migrate = Migrate()
bcrypt = Bcrypt()
def create_app():
app = Flask(__name__, static_url_path='', static_folder='./../react/public')
app.config.from_object(os.getenv("APP_SETTINGS", "config.Development"))
import modelref
db.init_app(app)
migrate.init_app(app, db)
bcrypt.init_app(app)
return app
And them my main.py file simply invokes the create_app method and then sets up routes and API resources like so:
main.py:
from flask import send_from_directory
from flask_cors import CORS
from apiref import load_api
from app import create_app
app = create_app()
CORS(app)
#app.route("/", defaults={'path':''})
def serve(path):
return send_from_directory(app.static_folder,'index.html')
load_api(app)
Running import modelref within the create_model method makes all the models imported in modelref visible to the app and the flask db commands.
How can I integrate flask-sqlalchemy with Google Cloud Functions and something like Cloud SQL?
Looking at the minimal application example, it wraps the app variable:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
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, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
def __repr__(self):
return '<User %r>' % self.username
But my Google Cloud Function doesn't have access to anything called app:
def my_function(request):
return "Hello world!"
Creating my own app with app = Flask(__name__) doesn't work as this app isn't used by Cloud Functions to return the response.
You can use flask.current_app instead. You'll also have to configure flask-sqlalchemy to use the Unix Domain Socket provided by CloudSQL:
from flask import current_app as app
from flask_sqlalchemy import SQLAlchemy
# Set these in the environment variables for the function
db_user = os.environ.get('CLOUD_SQL_USERNAME')
db_password = os.environ.get('CLOUD_SQL_PASSWORD')
db_name = os.environ.get('CLOUD_SQL_DATABASE_NAME')
db_connection_name = os.environ.get('CLOUD_SQL_CONNECTION_NAME')
# This is for Postgres, it's similar for MySQL
app.config['SQLALCHEMY_DATABASE_URI'] = f'postgresql://{db_user}:{db_password}#/{db_name}?host=/cloudsql/{db_connection_name}'
# This must be set, determine which is best for you
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
...
Then you can query the User model in your function:
def my_function(request):
user = User.query.first()
return f"Hello {user.username)!"
I am trying to filter data by username in Python and sql alchemy but somehow the filter is not working., i am able to filter by ID and add users., in below code, i just showed the method for filter by username. not sure what wrong i am doing
from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
import os
app = Flask(__name__)
basedir = os.path.abspath(os.path.dirname(__file__))
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir,
'datas.sqlite')
db = SQLAlchemy(app)
ma = Marshmallow(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)
def __init__(self, username, email):
self.username = username
self.email = email
class UserSchema(ma.Schema):
class Meta:
# Fields to expose
fields = ('username', 'email')
user_schema = UserSchema()
users_schema = UserSchema(many=True)
#endpointtogetuserdetailbyusername
#app.route("/username/<username>",methods=["GET"])
def user_detailbyusername(username):
user=User.query.filter(User.username=='Tim')
# i tried user=User.query.filter_by(User.username=='Tim') but no luck
return user_schema.jsonify(user)
if __name__ == '__main__':
app.run(debug=True)
I think you should add some method to really get back the records, something like User.query.filter(User.username=='Tim').all() or User.query.filter(User.username=='Tim').first(), as can be seen in the examples at http://flask-sqlalchemy.pocoo.org/2.3/queries/#querying-records
I have read quite a bit of documentation and I can't see what is wrong with these lines
update_this = User.query.filter_by(email=email).first()
update_this.emailconfirmed = True
db.session.commit()
...and yet when I deploy the boolean column 'emailconfirmed' never is update to True. I have confirmed with print statements that update_this.emailconfirmed is False at the exact point in the code shown above... I just can't seem to update that value. Does anybody know what tests I can do, what imports I should check etc. etc.
Right now this is the top of my main .py file where the above code appears
from flask import Flask, render_template, request, session, redirect, url_for, make_response
# the following line imports from models.py
from models import db, User
# the following line imports SignupForm from forms.py
from forms import SignupForm, LoginForm
from flask_mail import Mail, Message
from itsdangerous import URLSafeTimedSerializer
# Production (causes Heroku to redirect to SSL)
from flask_sslify import SSLify
from flask_sqlalchemy import SQLAlchemy
import os
app = Flask(__name__)
sslify = SSLify(app)
sslify = SSLify(app, subdomains=True)
app.config.from_pyfile('config_file.cfg')
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ['DATABASE_URL']
db = SQLAlchemy(app)
mail = Mail(app)
ts = URLSafeTimedSerializer(app.config['SECRET_KEY'], salt=app.config['SALT'])
and this is my models.py file
from flask_sqlalchemy import SQLAlchemy
from werkzeug import generate_password_hash, check_password_hash
db = SQLAlchemy()
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
firstname = db.Column(db.String(100))
lastname = db.Column(db.String(100))
role = db.Column(db.String(20))
roleapproved = db.Column(db.Boolean)
school = db.Column(db.String(100))
email = db.Column(db.String(120), unique=True)
emailconfirmed = db.Column(db.Boolean)
pwdhash = db.Column(db.String(100))
def __init__(self, firstname, lastname, role, school, email, password):
self.firstname = firstname.title()
self.lastname = lastname.title()
self.role = role.lower()
if role.lower() == 'student':
self.roleapproved = True
if role.lower() == 'teacher':
self.roleapproved = False
self.school = school.title()
self.email = email.lower()
self.set_password(password)
self.emailconfirmed = False
def set_password(self, password):
self.pwdhash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.pwdhash, password)
def __repr__(self):
return '<User {0}>'.format(self.email)
Any help on doing the update I mentioned above would be greatly appreciated!!
Ideally you want to maintain a single session throughout your application lifecycle. This way it makes it easy to reason about and you avoid binding sessions to individual models.
Thanks #Ilja Everila
In main.py instead of initializing SQLAlchemy
you should write,
db.init_app(app)
Define a save instance method for your User model.
def save(self):
"""Saves model object instance
"""
db.session.add(self)
db.session.commit()
You can call this method to save the instance as
update_this.save()
Another way to update the entity is to get the specific object session before committing
from sqlachemy.orm import session
...
session = session.object_session(update_this)
session.commit()
This maybe a duplicate, but all the solutions to the other questions did not help me.
I am trying to modularize my code where I am moving my config files to a separate file called settings.py.
To run the code i am going running from my terminal "python3 manage.py shell"
Python not updating
then I execute
from flask_blog import db
from author.models import Author
db.create_all()
Hence the QUESTION is: Why is the database not being updated?
The three file I have that concern config are:
manage.py Handles the server set-up (in simple wordds)
settings.py Handles the database for now
models.py The database model
__init__.py The init file
The code is below is the settings.py
import os
SECRET_KEY = 'NOTHING FOR NOW'
DEBUG = True
DB_USERNAME = 'root'
DB_PASSWORD = '1234'
BLOG_DB_NAME = 'blog2'
DB_HOST = os.getenv('IP', '127.0.0.1')
DB_URI = 'mysql+pymysql://root#127.0.0.1:blog'
# DB_URI = 'mysql+pymysql://%s:%s#%s/%s' % (DB_USERNAME, DB_PASSWORD, DB_HOST, BLOG_DB_NAME)
SQLALCHEMY_DB_URI = DB_URI
SQLALCHEMY_TRACK_MODIFICATIONS = True
The other file called manage.py (below) which handles the basic project config to run.
import os, sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from flask.ext.script import Manager, Server
from flask_blog import app
manager = Manager(app)
manager.add_command("runserver", Server(
use_debugger = True,
use_reloader = True,
host = os.getenv('IP', '127.0.0.1'),
port = int(os.getenv('PORT', 5000))
))
if __name__ == "__main__":
manager.run()
And lastly the database model below. models.py
from flask_blog import db
class Author(db.Model):
id = db.Column(db.Integer, primary_key=True)
fullname = db.Column(db.String(80))
email = db.Column(db.String(35), unique=True)
username = db.Column(db.String(80), unique=True)
password = db.Column(db.String(80))
is_author = db.Column(db.Boolean)
def __init__(self, fullname, email, username, password, is_author=False):
self.fullname = fullname
self.email = email
self.username = username
self.password = password
self.is_author = is_author
def __repr__(self):
return '<Author %r>' % self.username
The __init__.py is below
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config.from_object('settings')
db = SQLAlchemy(app)
from blog import views
from author import views
If you want to see the entire project then click here
In settings.py, try replacing
SQLALCHEMY_DB_URI = DB_URI
with
SQLALCHEMY_DATABASE_URI = DB_URI
And DB_URI = 'mysql+pymysql://root#127.0.0.1:blog'
with
DB_URI = 'mysql+pymysql://root#127.0.0.1/blog'
according to connection URI format:
dialect+driver://username:password#host:port/database