How to apply migrations in django, pytest, postgres - django

How to auto-apply migrations to test database?
Error
django.db.utils.ProgrammingError: relation "users" does not exist
E LINE 1: ...ers"."phone_confirmed", "users"."last_login" FROM "users" WH...
pytest.ini
[pytest]
DJANGO_SETTINGS_MODULE = ma_saas.settings
addopts = --migrations
database_fixture.py
import pytest
from django.db import connections
import psycopg2
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT
def run_sql(sql):
conn = psycopg2.connect(database='postgres')
conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
cur = conn.cursor()
cur.execute(sql)
conn.close()
#pytest.yield_fixture(scope='session')
def django_db_setup():
from django.conf import settings
settings.DATABASES['default']['NAME'] = 'test_db'
run_sql('DROP DATABASE IF EXISTS test_db')
run_sql('CREATE DATABASE test_db')
yield
for connection in connections.all():
connection.close()
run_sql('DROP DATABASE test_db')
I think it is better to create a database and apply its migrations in the database_fixture better.

It was not so obvious.
But we should use django_db_blocker to run commands.
from django.core.management import call_command
#pytest.yield_fixture(scope='session') # or #pytest.fixture(scope='session')
def django_db_setup(django_db_blocker):
from django.conf import settings
test_database_name = 'test_db'
settings.DATABASES['default']['NAME'] = test_db
run_sql(f'DROP DATABASE IF EXISTS {test_db}')
run_sql(f'CREATE DATABASE {test_db}')
with django_db_blocker.unblock():
call_command('migrate', '--noinput')
yield
for connection in connections.all():
connection.close()
run_sql(f'DROP DATABASE {test_db}')

Related

Integrate flask-sqlalchemy into project

I am new comer of flask api. I am doing a flask restful api project which purpose is to search stores by store name that is kept in existing database online.
Now is is connect to database using psycopg2.
I would like to improve it by using flask-sqlalchemy. I have searched on the web and the solution seems to use Automap, that I learned from the tutorial. The problem I am facing now is how to integrate those codes into my existing project, like where should I put the settings like automap_base() into my existing codes. Here is my current file structure:
app.py
∟ models
∟ family_mart.py
∟ resources
∟ family_mart.py
app.py:
from flask import Flask
from flask_restful import Api
from resources.family_mart import FamilyMart
app = Flask(__name__)
app.secret_key = 'apple'
api = Api(app)
api.add_resource(FamilyMart, '/familymart/<string:name>')
if __name__ == '__main__':
app.run(port=5000, debug=True)
family_mart.py in models folder:
import psycopg2
from config import config
class FamilyMartModel:
#classmethod
def find_by_name(cls, name):
conn = None
try:
params = config()
conn = psycopg2.connect(**params)
cur = conn.cursor()
query = f"""
SELECT extract_date
, store_name
, address
FROM cnvnt_str_fm
WHERE store_name = '{name}'
"""
cur.execute(query)
rows = cur.fetchall()
cur.close()
if rows:
result = {'store':[]}
for row in rows:
append_dict = {'extract_date':row[0].strftime('%Y-%m-%d'),
'store_name':row[1],
'address':row[2]}
result['store'].append(append_dict)
return result
except (Exception, psycopg2.DatabaseError) as error:
print(error)
finally:
if conn is not None:
conn.close()
print('Database connection closed.')
family_mart.py in resources folder:
from flask_restful import Resource
from models.family_mart import FamilyMartModel
class FamilyMart(Resource):
def get(self, name):
item = FamilyMartModel.find_by_name(name)
if item:
return item
return {'message':'Store not found.'}, 404
After study the documentation, I restructured my project and integrate with sqlalchemy successfully. I removed model folder and added db.py to configure the existing database, here is the detail :
New file structure:
app.py
∟ resources
∟ family_mart.py
db.py
app.py:
from flask import Flask
from flask_restful import Api
from config import config
from db import db
from resources.family_mart import FamilyMart
params = config()
DB_FULL_URL = f"postgresql+psycopg2://{params['user']}:{params['password']}#{params['host']}/{params['database']}"
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = DB_FULL_URL
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.secret_key = 'apple'
api = Api(app)
api.add_resource(FamilyMart, '/familymart/<string:name>')
if __name__ == '__main__':
db.init_app(app)
app.run(port=5000, debug=True)
db.py:
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import MetaData
from sqlalchemy import Table, Column, Integer, String, DateTime
db = SQLAlchemy()
metadata_obj = MetaData()
fm_table = Table('cnvnt_str_fm', metadata_obj,
Column('extract_date', String),
Column('store_name', String),
Column('address', String)
)
family_mart.py:
from flask_restful import Resource
from db import db
from db import fm_table
class FamilyMart(Resource):
def get(self, name):
results = db.session.query(fm_table).filter_by(store_name=name).\
order_by(fm_table.c.extract_date.desc()).limit(3)
output = {'store':[]}
for row in results:
extract_date = str(row[0])
store_name = row[1]
address = row[2]
output['store'].append({'extract_date':extract_date,
'store_name':store_name,
'address':address})
return output

Flask csrf: key is missing with Blueprint

Hello everyone I have this issue I am not able to correct:
KeyError: 'A secret key is required to use CSRF.'
I am now using Flask with Blueprint.
I am not using CSRF at all but I think the LoginForm is.
I structured my project with Blueprint.
Before that, everything was find.
Here is my init.py file:
from flask import Flask
from flask_login import LoginManager
from flask_bcrypt import Bcrypt
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
from flask.config import Config
from flask_wtf.csrf import CSRFProtect
db = SQLAlchemy()
migrate = Migrate(db)
bcrypt = Bcrypt()
csrf = CSRFProtect()
login_manager = LoginManager()
login_manager.login_view = "login"
login_manager.login_message_category = "info"
from Flask import models
from Flask.models import User
admin = Admin(name='Admin')
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
admin.init_app(app)
db.init_app(app)
csrf.init_app(app)
login_manager.init_app(app)
migrate.init_app(app)
bcrypt.init_app(app)
db.init_app(app)
from Flask.users.routes import users
app.register_blueprint(users)
return app
This is my config.py file:
import os
class Config:
SECRET_KEY = "ef2006629e09b70e55a6fb95c4e3a538"
SQLALCHEMY_DATABASE_URI = "sqlite:///site.db"
# WTF_CSRF_SECRET_KEY= "bjk567nvhbvj63vg363vghvghv3768vgfbkijvr784"
# CSRF_ENABLED = True
Thank you for your help !
You should create a SECRET_KEY=<Your secret key here> property in your configuration. It must be a difficult string.
I found the problem
I was not calling properly my config.py file
In my init.py file I change the line:
from flask.config import Config
with the line:
from Flask.config import Config
Flask is the name of my file, which is different from flask.
I should have found another name

Flask, SQLAlchemy, Data Tables - ImportError: cannot import name 'db' in Models.py

I'm trying to use Data Tables with Flask and SQLAchemy and I'm facing the
ImportError: cannot import name 'db'
in module Models.py
My project tree:
app
/common
__init__.py
models.py
toutes.py
/mod_tables
---
__init__
config.py
__init__.py
from flask import Flask, redirect, session
from app.mod_tables.models import TableBuilder
from app.config import Config
from flask_sqlalchemy import SQLAlchemy
#from flask_migrate import Migrate
app = Flask(__name__)
table_builder = TableBuilder()
app.config.from_object(Config)
db = SQLAlchemy(app)
db.init_app(app)
#migrate = Migrate(app, db)
from app.common.routes import main
from app.common import models
from app.mod_tables.controllers import tables
# Register the different blueprints
app.register_blueprint(main)
app.register_blueprint(tables)
config.py
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config(object):
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'spbData-V3560-FRANCO.db')
SQLALCHEMY_TRACK_MODIFICATIONS = False
\common.models.py
from app import db
class TipoPoste(db.Model):
tp_tipo = db.Column(db.String(35), primary_key=True)
tp_descricao = db.Column(db.String(255))
def __repr__(self):
return '<Tipo Poste {} - {}>'.format(self.tp_tipo,
self.tp_descricao)
Thos code gives me the following error:
flask.cli.NoAppException flask.cli.NoAppException: While importing
"app", an ImportError was raised:
Traceback (most recent call last): File
"c:\users\rfran.v3560-franco\appdata\local\programs\python\python36-32\lib\site-packages\flask\cli.py",
line 235, in locate_app
__import__(module_name) File "C:\Users\rfran.V3560-FRANCO\OneDrive\ArquivosLocais\gepoc\app\__init__.py",
line 2, in <module>
from app.mod_tables.models import TableBuilder File "C:\Users\rfran.V3560-FRANCO\OneDrive\ArquivosLocais\gepoc\app\mod_tables\models.py",
line 3, in <module>
from app.common.models import TipoPoste File "C:\Users\rfran.V3560-FRANCO\OneDrive\ArquivosLocais\gepoc\app\common\models.py",
line 1, in <module>
from app import db ImportError: cannot import name 'db'
Any hint? Thanks in advance.
Your error is caused by a circular import.
db is imported from app/init.py into the app/common/models.py module then the entire models.py module, including the db object, is imported into the app/init.py module. This is a circular import of the db object.
Instead, import the specific objects from the models.py file that you need:
init.py
...
from app.common.routes import main
from app.common.models import TipoPoste
from app.mod_tables.controllers import tables
...
That should fix it.
A good practice is not importing entire modules as you've done here. This could cause conflicts in some names that you may not be obvious at first. It'll save you lots of debugging time.
Try move db object into models.py. I omitted some unnecessary code.
__init__.py
...
from app.common.models import db
...
app = Flask(__name__)
db.init_app(app)
...
models.py
...
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
...

Flask Manager Command #manager.command doesn't work

I have a problem here.
I want to populate a table in the database with a record when the migration is executed, but actually it doesn't work. I'm using SQLAlchemy for the DB and the Manager object to create the DB.
In the DB I have a table called 'skills' which contains objects of type 'Skill'.
I just want to fill the table with a record during the initialization phase.
Please help me in finding the solution.
manage.py
#!/usr/bin/env python
import os
from app import create_app, db
from app.models import Skill
from flask_script import Manager, Shell
from flask_migrate import Migrate, MigrateCommand
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
manager = Manager(app)
migrate = Migrate(app, db)
def make_shell_context():
return dict(app=app, db=db, Skill=Skill)
manager.add_command("shell", Shell(make_context=make_shell_context))
manager.add_command('db', MigrateCommand)
#manager.command
def create_skill():
db.session.add(Skill(name='ciaociao', in_use=False))
db.session.commit()
if __name__ == '__main__':
manager.run()
init.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from config import config
db = SQLAlchemy()
login_manager = LoginManager()
login_manager.session_protection = 'strong'
login_manager.login_view = 'auth.login'
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
db.init_app(app)
login_manager.init_app(app)
return app
models.py
class Skill(db.Model):
__tablename__ = 'skills'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), index=True)
in_use = db.Column(db.Boolean, default=False)

Flask-migrate multiple models.py

I've a question related to Flask-migrate.
I'm creating a set of web services with Flask. I've split each web service in his own package in a python application.
The application structure look like this:
MyApp
WS1
models.py
WS2
models.py
CommonPackage
models.py
How can I import all the modules and init the db? I've tried to import them all manually but is not working.
I know that it works if I import the "app" from WS1 or Ws2 separately, but I would like to do this in a single operation, is this possible?
Here you can find the code for Flask-migrate:
#!virtualenv/bin/python
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager
from config import SQLALCHEMY_DATABASE_URI
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = SQLALCHEMY_DATABASE_URI
db = SQLAlchemy(app)
migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command('db', MigrateCommand)
from WS1.models import Class1, Class2, Class3 <--- This is not importing
from WS2.models import Class4, Class5, Class6 <--- This is not importing
if __name__=='__main__':
manager.run()
This is the modules where I initialize SQLAlchemy session:
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from config import SQLALCHEMY_DATABASE_URI
engine = create_engine(SQLALCHEMY_DATABASE_URI, convert_unicode = True)
db_session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
Base = declarative_base()
Base.query = db_session.query_property()
All the models import this module and inherit from Base.
Thanks a lot,
Enrico
When you are defining a custom declarative base you're not really using Flask-SQLAlchemy anymore—and Flask-Migrate reads the models from Flask-SQLAlchemy's internal declarative base. (That is why you have to initialize Migrate with both app and db).
Flask has an answer to the import cycle problem: Most Flask extensions have an .init_app() method to defer initializing the extension where necessary.
In the module where you currently create Base by hand, just do db = SQLAlchemy() and then in your models import db and use db.Models as your declarative base instead of Base. In your app.py, do the following:
from dbpackagename import db
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = SQLALCHEMY_DATABASE_URI
db.init_app(app)