Whenever I try to import init db in the python console:
>>from app.database import init_db
>>init_db()
init db
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "app\database.py", line 18, in init_db
import models
ImportError: No module named models
This is the content of my app.database function:
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()
def init_db():
# import all modules here that might define models so that
# they will be registered properly on the metadata. Otherwise
# you will have to import them first before calling init_db()
print "init db"
import models
Base.metadata.create_all(bind=engine)
Why is it like this?
UPDATE: This is the structure of my project.
app/
filebase/
init.py
models.py
users/
init.py
models.py
projects/
init.py
models.py
init.py
database.py (this is where the init_db was defined
As for the structure on why it looked like that, the reason is to make the project modular in its form.
You need the parent directory of models.py (or, of the models package) to be in your sys.path. You can put it there in your console session, or, in the shell, add it to environment variable PYTHON_PATH so it will be set at entry to the console session.
Related
I am building a small questionnaire with Flask and SQLAlchemy, and for development testing I try to recall a random form by it's id. The terminal gives an ImportError however, but I do not have any files named after modules (like other stackoverflow problems) or circular import as the error suggests. How can I query the AddEnqueteForm in this separate file?
enquete_algorithm.py:
from models import AddEnqueteForm
import random
from settings import *
from flask import request
def enq_results(form_id):
form = AddEnqueteForm.query.all(filter=form_id)
reliability_dict = {}
reliability_dict['stelling_set_1'] = algorithm(form.stelling1, form.stelling2)
And of course models.py:
from main import db
class AddEnqueteForm(db.Model):
id = db.Column(db.Integer, primary_key=True)
stelling1 = db.Column(db.String(4), nullable=False)
stelling2 = db.Column(db.String(4), nullable=False)
Error:
Traceback (most recent call last):
File "c:\Users\Enquete\enquete_algorithm.py", line 1, in <module>
from mods import AddEnqueteForm
File "c:\Users\Enquete\models.py", line 1, in <module>
from main import db
File "c:\Users\Enquete\main.py", line 9, in <module>
from routes import *
File "c:\Users\Enquete\routes.py", line 4, in <module>
from models import AddEnqueteForm
ImportError: cannot import name 'AddEnqueteForm' from partially initialized module 'models' (most likely due to a circular import) (c:\Users\Enquete\models.py)
I realized some variable names are a bit confusing: the AddEnqueteForm is a model, not a form. I built the forms entirely in HTML.
The problem is not with enquete_algorithm.py .The circular import is occurs between routes.py and models.py. You are importing main.py in models.py and main.py in place imports routes.py and routes.py is importing models.py.
For solving this I made a separate file database.py where I define the db and I import db from that file in both main.py and models.py. This prevents the circular import.
database.py:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def init_app(app):
db.init_app(app)
models.py :
from database import db
class AddEnqueteForm(db.Model):
id = db.Column(db.Integer, primary_key=True)
stelling1 = db.Column(db.String(4), nullable=False)
stelling2 = db.Column(db.String(4), nullable=False)
In main.py you need to import both models and database and use init_app() to initialize the database.
[...]
from database import db
from models import *
[...]
app = Flask(__name__)
database.init_app(app)
Now if you ever want to run command db.create_all() through terminal to create database. you need to use application context.
running the following commands will create the database.
from main import *
with app.app_context():
db.create_all()
I am trying to setup a database just like in a tutorial but I am getting a programming error that a table doesn't exist when I'm trying to add a User
This is the file that errors (database.py):
from sqlalchemy import create_engine, MetaData
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine(
"mysql+pymysql://testuser:testpassword#localhost/test?charset=utf8",
connect_args = {
"port": 3306
},
echo="debug",
echo_pool=True
)
db_session = scoped_session(
sessionmaker(
bind=engine,
autocommit=False,
autoflush=False
)
)
Base = declarative_base()
def init_db():
import models
Base.metadata.create_all(bind=engine)
from models import User
db_session.add(
User(username="testuser", password_hash=b"", password_salt=b"", balance=1)
)
db_session.commit()
print("Initialized the db")
if __name__ == "__main__":
init_db()
To init the database (create the tables) I just run the file.
It errors when it creates the test user.
Here is models.py:
from sqlalchemy import Column, Integer, Numeric, Binary, String
from sqlalchemy.orm import relationship
from database import Base
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
username = Column(String(16), unique=True)
password_hash = Column(Binary(32))
password_salt = Column(Binary(32))
balance = Column(Numeric(precision=65, scale=8))
def __repr__(self):
return "<User(balance={})>".format(balance)
I tried:
Committing before adding users (after create_all)
Drop existing tables from the database (although it seems like the table never gets committed)
from models import User instead of import models (before create_all)
Sorry if there are so many simillar questions, I promise I scavenged for answers, but it's always silly mistakes I made sure I didn't make (or atleast the ones I saw).
I am using MariaDB.
Sorry for long post, many thanks in advance.
The Base in database.py isn't the same Base that is imported into models.py.
A simple test is to put a print('creating Base') function call just above the Base = declarative_base() statement, and you'll see it is being created twice.
Python calls the module that is being executed '__main__', which you know as you have the if __name__ == '__main__' conditional at the bottom of your module. So the first Base that is created is __main__.Base. Then, in models.py, from database import Base causes the database module to be parsed again, creating database.Base in the namespace, and that is the Base from which User inherits. Then back in database.py, the Base.metadata.create_all(bind=engine) call is using the metadata from __main__.Base which has no tables in it, and as such creates nothing.
Don't execute out of the module that creates the Base instance. Create another module called main.py (or whatever), and move your init_db() function there and import Base, db_session and engine from database.py into main.py. That way, you are always using the same Base instance. This is example of main.py:
from database import Base, db_session, engine
from models import User
def init_db():
Base.metadata.create_all(bind=engine)
db_session.add(
User(username="testuser", password_hash=b"", password_salt=b"", balance=1)
)
db_session.commit()
print("Initialized the db")
if __name__ == "__main__":
init_db()
Declare Base class once(for each database) & import it to all modules which define table classes (inherited from Base)
For Base (a metaclass) to scan & find out all classes which are inherited from it, we need to import all the modules where such table classes (inherited from Base) are defined to module where we call Metadata.create_all(engine).
You need to import the relevant model where you call "Base.metadata.create_all". Example below to create user table
from ModelBase import Base
from UserModel import User
def create_db_schema(engine):
Base.metadata.create_all(engine,checkfirst=True)
I am working on adding flask admin to a preexisting flask boiler plate project. I've been able to get the basic project working at https://github.com/kc1/flask-base (SCREENSHOT). I now need to add modelviews to add basic CRUD functionality. To do this I changed the code to :
adm = Admin(app,name='flaskadmin')
from app.models import User
adm.add_view(ModelView(User, db.session))
You can see that it works. But if I import the User model with the rest of the imports at the top of app/init I get:
Traceback (most recent call last):
File "...flask-base/manage.py", line 10, in <module>
from app import create_app, db
File "E:\ENVS\r3\flask-base\app\__init__.py", line 17, in <module>
from app.models import User
File "E:\ENVS\r3\flask-base\app\models\__init__.py", line 6, in <module>
from .user import * # noqa
File "E:\ENVS\r3\flask-base\app\models\user.py", line 7, in <module>
from .. import db, login_manager
ImportError: cannot import name 'db'
Why?
User is a Flask-SQLAlchemy model that wraps models using SQLalchemy's API. It inherits all of its models from a db object, which I assume you are instantiating or registering inside a create_app method.
So, you should have something like this
db = SQLAlchemy()
def create_app(config):
app = Flask(__name__)
db.init_app(app)
adm = Admin(app,name='flaskadmin')
from app.models import User
adm.add_view(ModelView(User, db.session))
return app
Whenever you import User from user.py you are basically importing db.Model, which requires for db to exists and actually contain data. Be wary of circular imports in Flask and in Python in general.
The error you are receiving is clearly stated in the error traceback
File "E:\ENVS\r3\flask-base\app\models\user.py", line 7, in <module>
from .. import db, login_manager
ImportError: cannot import name 'db'
This is, in user.py there is an import of db from ../__init__.py but in that file there is an import of User happening before the definition of db.
A db object emulates the declarative approach from SQLAlchemy, where the object holds data about every other Class that inherited from it.
I'm trying to build a webapp with flask, Mysql, SQLAlchemy and Alembic. But I'm not able to understand how imports work in python and how to set up my target_metadata to be able to use revision --autogenerate
Here is my directory's tree:
My website's init look like this:
import os
from flask import Flask
app = Flask(__name__, static_folder=os.path.join(os.path.dirname(os.path.abspath(__file__)), '../static'))
app.config.from_pyfile('config.py', silent=True)
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine(app.config['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()
#app.teardown_appcontext
def shutdown_session(exception=None):
db_session.remove()
from website import views
Then in my env.py when I try to import my Base like this:
from website import Base
target_metadata = Base.metadata
and try to run alembic revision --autogenerate ... I get this error:
ImportError: No module named website.
And When I try to import Baselike this:
from website import Base
target_metadata = Base.metadata
I get this error: ValueError: Attempted relative import in non-package.
Please can you help me to understand how import works in python and how can I set my target_metadata ?
I've just recently had this problem myself, though not with flask. What worked for me is simple, but it seems to be necessary (the current directory isn't on the pythonpath, so when you do from website import Base, python is throwing an exception because it can't find the website module).
Try adding this at the top of your env.py module:
import os
import sys
sys.path.append(os.getcwd())
It's a really hacky way to do it, but it works for me.
Also, just curious here... Is there any reason you aren't using libraries that do just about all of this for you? Think ones like flask-sqlalchemy, flask-migrate or flask-alembic (I forget which, but it wraps alembic for you).
If your unaware of these, you might want to check out the flask extension registry. Some really handy ones there.
My project is based on Pyramid.
Pyramid's default scaffold is very simple: view.py, models.py, and so on.
I created some directories to replace them. For example:
view.py --> view directory
models.py --> model directory
Then I create a file named login.py:
from pyramid.httpexceptions import HTTPForbidden
from pyramid.response import Response
from pyramid.view import view_config
import logging
from ..model import (
DBSession,
DynUser,
)
log = logging.getLogger(__name__)
def find_user(account):
#user = None
try:
user = DBSession.query(DynUser).filter(DynUser.username==account).first()
#one = DBSession.query(MyModel).filter(MyModel.name=='one').first()
except ValueError:
#log.warning("invalidate id %s input." % request.matchdict['id'])
log.warning("invalidate id %s input.")
except Exception:
log.error("database error!")
if not user:
return HTTPForbidden()
return dict(user=user)
I have imported DBSession. Why do I still get this error?
tip
--------------------------------------------------------------------------
Undefined variable from import: DBSession
Undefined variable from import: DBSession
DBSession Found at: dyncms.model.meta
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
You didn't mention it, but I'll assume you moved models.py to model/__init__.py.
from ..models only works if login.py is in a subdir (like login/login.py). If it's not the case, you should use from .models import.
In model directory
__init__.py
# package
from .meta import DBSession
from .meta import Base
from .book import Book
meta.py
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import (
scoped_session,
sessionmaker,
)
from zope.sqlalchemy import ZopeTransactionExtension
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
Base = declarative_base()