AttributeError when importing flask-SQLAlchemy model - python

I am following this tutorial to build a JWT based authentication system.
app.py:
from flask import Flask
from flask_restful import Api
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SECRET_KEY'] = 'd0cad49580952003e6ae01499c7bb190a4b4f9a5babd866f47064707f7b78506'
api = Api(app)
db = SQLAlchemy(app)
#app.before_first_request
def create_tables():
db.create_all()
import resources, models
api.add_resource(resources.UserRegistration, '/registration')
if __name__ == '__main__':
app.run()
models.py:
from app import db
class UserModel(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
password = db.Column(db.String(150), nullable=False)
email = db.Column(db.String(100), unique=True, nullable=False)
def __init__(self, name, password, email):
self.name = name
self.password = password
self.email = email
#classmethod
def find_by_username(cls, username):
return cls.query.filter_by(username=username).first()
def save_to_db(self):
db.session.add(self)
db.session.commit()
resources.py:
from flask_restful import Resource, reqparse
from models import UserModel
class UserRegistration(Resource):
def post(self):
parser = reqparse.RequestParser()
parser.add_argument('name', help='This field cannot be blank', required=True)
parser.add_argument('email', help='This field cannot be blank', required=True)
parser.add_argument('password', help='This field cannot be blank', required=True)
data = parser.parse_args()
if UserModel.find_by_username(data['name']):
return {'message': 'User {} already exists'.format(data['name'])}
new_user = UserModel(
name=data['name'],
password=data['password'],
email=data['email']
)
try:
new_user.save_to_db()
return {
'status': 'User {} was created'.format(data['username'])}
except:
return {'message': 'Something went wrong'}, 500
When I run app.py, I get the following error:
Traceback (most recent call last):
File "G:\python\PycharmProjects\vumonic\app.py", line 19, in <module>
import resources, models
File "G:\python\PycharmProjects\vumonic\resources.py", line 2, in <module>
from models import UserModel
File "G:\python\PycharmProjects\vumonic\models.py", line 1, in <module>
from app import db
File "G:\python\PycharmProjects\vumonic\app.py", line 21, in <module>
api.add_resource(resources.UserRegistration, '/registration')
AttributeError: module 'resources' has no attribute 'UserRegistration'
This error dissapears when I remove from models import UserModel from resources.py.
I cannot figure out the reason for the error.
I am using Flask==1.1.2, Flask-SQLAlchemy==2.4.4 and Flask-RESTful==0.3.8
This is the first time Iam developing an API so any help would be appreciated.

you are facing circular import issue.
When Python imports a module, it checks the module registry to see if the module was already imported. If the module was already registered, Python uses that existing object from cache. The module registry is a table of modules that have been initialized and indexed by module name. This table can be accessed through sys.modules.
If it was not registered, Python finds the module, initializes it if necessary, and executes it in the new module's namespace.
to know more about circular import you can read the article:
https://stackabuse.com/python-circular-imports/
https://www.stefaanlippens.net/circular-imports-type-hints-python.html
this tutorial of Miguel Grinberg is a life savior
https://www.youtube.com/watch?v=NH-8oLHUyDc&t=3205s

Related

flask_sqlalchemy query returns ImportError

I have a Flask web application that has an sqlite database called data.db which contains the data of user/admin. Whenever I try to query the data on my python file routes.py it returns an ImportError message. However, when I query the data from my conda terminal it works properly, I got the desired result. Is there's any solution for this? I wonder what I did wrong
this is the init.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_bcrypt import Bcrypt
app = Flask(__name__)
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'admin_login'
app.config['SECRET_KEY'] = 'my secret key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.db'
from yves_letters import routes
this is the models.py
from yves_letters import db
from flask_login import UserMixin
class Admin(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True)
display_name = db.Column(db.String(20))
password = db.Column(db.String(100), nullable=False)
def __repr__(self):
return f'id: {self.id}, username: {self.username}, display_name: {self.display_name}, password: {self.password}'
this is the routes.py
from yves_letters.models import Data, Admin
#app.route('/')
def home():
admin = Admin.query.all()
return render_template('home.html')
this is the error I'm getting while running the code
ImportError
ImportError: DLL load failed: The specified module could not be found.
Traceback (most recent call last)
File "C:\Users\USER\anaconda3\envs\webapp_env\lib\site-packages\sqlalchemy\util\_collections.py", line 1020, in __call__
Open an interactive python shell in this framereturn self.registry[key]
On the other hand, when I do the query from my conda terminal it's working well.
admin = Admin.query.all()
admin
[id: 1, username: admin, display_name: Mac Yves]
Is there's something I missed?

AttributeError: module 'app.models.PostModel' has no attribute '_sa_class_manager'

I was trying to add flask-admin to my application. I am getting the following error
Traceback (most recent call last):
File "/run/media/sanifss/4264239664238C2B/Projects/Personal/Portfolio/run.py", line 3, in <module>
app = create_app()
File "/run/media/sanifss/4264239664238C2B/Projects/Personal/Portfolio/app/__init__.py", line 71, in create_app
admin.add_view(ModelView(PostModel, db.session))
File "/home/sanifss/.local/share/virtualenvs/Portfolio-UVOeSvIO/lib/python3.9/site-packages/flask_admin/contrib/sqla/view.py", line 327, in __init__
super(ModelView, self).__init__(model, name, category, endpoint, url, static_folder,
File "/home/sanifss/.local/share/virtualenvs/Portfolio-UVOeSvIO/lib/python3.9/site-packages/flask_admin/model/base.py", line 818, in __init__
self._refresh_cache()
File "/home/sanifss/.local/share/virtualenvs/Portfolio-UVOeSvIO/lib/python3.9/site-packages/flask_admin/model/base.py", line 895, in _refresh_cache
self._list_columns = self.get_list_columns()
File "/home/sanifss/.local/share/virtualenvs/Portfolio-UVOeSvIO/lib/python3.9/site-packages/flask_admin/model/base.py", line 1035, in get_list_columns
only_columns=self.column_list or self.scaffold_list_columns(),
File "/home/sanifss/.local/share/virtualenvs/Portfolio-UVOeSvIO/lib/python3.9/site-packages/flask_admin/contrib/sqla/view.py", line 418, in scaffold_list_columns
for p in self._get_model_iterator():
File "/home/sanifss/.local/share/virtualenvs/Portfolio-UVOeSvIO/lib/python3.9/site-packages/flask_admin/contrib/sqla/view.py", line 354, in _get_model_iterator
return model._sa_class_manager.mapper.iterate_properties
AttributeError: module 'app.models.PostModel' has no attribute '_sa_class_manager'
I think it could be some dependency issue between SQLAlchemy and Flask-Admin. But cannot figure out what exactly is going wrong and how to fix.
My PostModel
# src/models/PostModel.py
import datetime
from marshmallow import Schema, fields
from slugify import slugify
from . import db
class PostModel(db.Model):
"""
Post Model
"""
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(128), nullable=False, unique=True)
slug = db.Column(db.String(128), nullable=False)
contents = db.Column(db.Text, nullable=False)
owner_id = db.Column(
db.Integer,
db.ForeignKey('users.id'),
nullable=False)
created_at = db.Column(db.DateTime)
modified_at = db.Column(db.DateTime)
def __init__(self, data):
self.title = data.get('title')
self.slug = slugify(data.get('title'))
self.contents = data.get('contents')
self.owner_id = data.get('owner_id')
self.created_at = datetime.datetime.utcnow()
self.modified_at = datetime.datetime.utcnow()
def save(self):
db.session.add(self)
db.session.commit()
def update(self, data):
for key, item in data.items():
setattr(self, key, item)
self.modified_at = datetime.datetime.utcnow()
db.session.commit()
def delete(self):
db.session.delete(self)
db.session.commit()
#staticmethod
def get_all_blogposts():
return PostModel.query.all()
#staticmethod
def get_one_post(id):
return PostModel.query.get(id)
#staticmethod
def get_one_post_by_slug(id):
return PostModel.query.filter_by(slug=id).first()
def __repr__(self):
return '<id {}>'.format(self.id)
Db is created under models/init.py
# app/models/__init__.py
from flask_bcrypt import Bcrypt
from flask_sqlalchemy import SQLAlchemy
# initialize our db
db = SQLAlchemy()
bcrypt = Bcrypt()
at last my app
import logging.config
from os import environ
from celery import Celery
from dotenv import load_dotenv
from flask import Flask
from flask_admin import Admin
from flask_admin.contrib import sqla
from flask_cors import CORS
from app.models import PostModel, UserModel
from .config import config as app_config
from .models import bcrypt, db
celery = Celery(__name__)
def create_app():
"""
Create app
"""
# loading env vars from .env file
load_dotenv()
APPLICATION_ENV = get_environment()
logging.config.dictConfig(app_config[APPLICATION_ENV].LOGGING)
# app initializations
app = Flask(app_config[APPLICATION_ENV].APP_NAME)
app.config.from_object(app_config[APPLICATION_ENV])
CORS(app, resources={r'/api/*': {'origins': '*'}})
celery.config_from_object(app.config, force=True)
# celery is not able to pick result_backend and hence using
# update
celery.conf.update(
result_backend=app.config['RESULT_BACKEND'])
# Other initializations
bcrypt.init_app(app)
db.init_app(app)
# migration
from flask_migrate import Migrate
migrate = Migrate(app, db)
# Blueprints
from .views.CoreView import core as core_blueprint
app.register_blueprint(
core_blueprint,
url_prefix='/api/v1/core'
)
from .views.UserView import user_api as user_blueprint
app.register_blueprint(
user_blueprint,
url_prefix='/api/v1/users'
)
#app.route('/', methods=['GET'])
def index():
"""
Blog endpoint
"""
return 'Server is up and running'
# Admin
admin = Admin(app)
admin.add_view(sqla.ModelView(PostModel, db.session))
return app
def get_environment():
return environ.get('APPLICATION_ENV') or 'development'
I am new to flask and flask-admin any help is much appreciated.

Getting Error: cannot import name 'DB' from partially initialized module. Not finding the circular import issue

I'm working on a assignment for school.
But When I try to run the flask app I get some kind of circular import error. I've been trying to go back and remove things step by step while still keeping my project functional. I've hit a wall:
Usage: main.py run [OPTIONS]
Error: While importing 'Help.app', an ImportError was raised:
Traceback (most recent call last): FileUsage: main.py run [OPTIONS]
Error: While importing 'Help.app', an ImportError was raised:
Traceback (most recent call last): File
"/home/chris/Help/venv/lib/python3.8/site-packages/flask/cli.py", line
256, in locate_app
__import__(module_name) File "/home/chris/Help/__init__.py", line 1, in <module>
from .app import create_app
File "/home/chris/Help/app.py", line 5, in
from .models import DB, User, insert_example_users
File "/home/chris/Help/models.py", line 3, in
from .twitter import add_or_update_user
File "/home/chris/Help/twitter.py", line 5, in
from .models import DB, Tweet, User
ImportError: cannot import name 'DB' from partially initialized module
'Help.models' (most likely due to a circular import)
(/home/chris/Help/models.py)
app.py file:
# Main app/routing file for Twitoff
from os import getenv
from flask import Flask, render_template
from .models import DB, User, insert_example_users
# creates application
def create_app():
# Creating and configuring an instance of the Flask application
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = getenv("DATABASE_URI")
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
DB.init_app(app)
# TODO - Make rest of application
#app.route('/')
def root():
DB.drop_all()
DB.create_all()
insert_example_users()
return render_template("base.html", title="home", users=User.query.all())
#app.route("/update")
def update():
insert_example_users()
return render_template("base.html", title="home", users=User.query.all())
#app.route("/reset")
def reset():
DB.drop_all()
DB.create_all()
return render_template(
"base.html",
title="home",
)
return app
Twitter.py file:
"""Retrieve tweets and users then create embeddings and populate DB"""
from os import getenv
import tweepy
import spacy
from .models import DB, Tweet, User
# TODO - Don't include raw keys and tokens (create .env file)
TWITTER_API_KEY = getenv("TWITTER_API_KEY")
TWITTER_API_SECRET_KEY = getenv("TWITTER_API_SECRET_KEY")
TWITTER_OAUTH = tweepy.oAuthHandler(TWITTER_API_KEY, TWITTER_API_SECRET_KEY)
TWITTER = tweepy.API(TWITTER_OAUTH)
# NLP model
nlp = spacy.load("my_model")
def vectorize_tweet(tweet_text):
return nlp(tweet_text).vector
def add_or_update_user(username):
try:
twitter_user = TWITTER.get_user(username)
db_user = (User.query.get(twitter_user.id)) or User(
id=twitter_user.id, name=username
)
DB.session.add(db_user)
tweets = twitter_user.timeline(
count=200, exclude_replies=True, include_rts=False, tweet_mode="extended"
)
if tweets:
db_user.newest_tweet_id = tweets[0].id
for tweet in tweets:
vectorized_tweet = vectorize_tweet(tweet.full_text)
db_tweet = Tweet(id=tweet.id, text=tweet.full_text, vect=vectorized_tweet)
db_user.tweets.append(db_tweet)
DB.session.add(db_tweet)
DB.session.commit()
except Exception as e:
print(f"Error processing {username}: {e}")
raise e
models.py file:
""""SQLAlchemy models and utility functions for Twitoff Application"""
from flask_sqlalchemy import SQLAlchemy
from .twitter import add_or_update_user
DB = SQLAlchemy()
class User(DB.Model):
"""Twitter User table that will correspond to tweets - SQLAlchemy syntax"""
id = DB.Column(DB.BigInteger, primary_key=True)
name = DB.Column(DB.String, nullable=False)
newest_tweet_id = DB.Column(DB.BigInteger)
def __repr__(self):
return f"<User:{self.name}>"
class Tweet(DB.Model):
"""tweet text data - associated with User table"""
id = DB.Column(DB.BigInteger, primary_key=True)
text = DB.Column(DB.Unicode(290))
vect = DB.Column(DB.PickleType, nullable=False)
user_id = DB.Column(DB.BigInteger, DB.ForeignKey("user.id"), nullable=False)
user = DB.relationship("User", backref=DB.backref('tweets', lazy=True))
def __repr__(self):
return f"<Tweet: {self.text}"
def insert_example_users():
"""We will get an error if we run this twice without dropping & creating"""
users = ["elonmusk", "geoffkeighley", "iamjohnoliver", "neiltyson"]
for user in users:
DB.session.add(add_or_update_user(user))
DB.session.commit()
init.py file:
from .app import create_app
APP = create_app()
You could place the app instance not in __init__.py but in app.py. The instance should be a WSGI callable and not a package identifier.

How can i both register blueprint and add that app to flask-admin

my Code:
__init__.py
from flask import Flask
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config.from_object('config')
db = SQLAlchemy(app)
from user.user import mod as user
from user.models import User as userModel
app.register_blueprint(user, url_prefix='/user')
admin = Admin(app, name='My app')
admin.add_view(ModelView(userModel, db.session, name='userAdmin'))
user.py:
from flask import Blueprint, json
from flask.views import MethodView
mod = Blueprint('user', __name__)
class UserAPI(MethodView):
def get(self):
users = [
{'nickname': 'Chan'},
{'nickname': 'Hzz'},
]
return json.dumps(users)
mod.add_url_rule('/users/', view_func=UserAPI.as_view('users'))
models.py:
from app import db
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 %s>" % self.username
i have a blueprint in my user app, and i've registered it, but when i want to add this to my admin to manage the user data, it throws the below exception:
Traceback (most recent call last):
File "run.py", line 2, in <module>
from app import app
File "/home/chenhj/flask/multiapp/app/__init__.py", line 21, in <module>
admin.add_view(ModelView(userModel, db.session, name='chj'))
File "/home/chenhj/.virtualenvs/multiapp/local/lib/python2.7/site-packages/flask_admin/base.py", line 526, in add_view
self.app.register_blueprint(view.create_blueprint(self))
File "/home/chenhj/.virtualenvs/multiapp/local/lib/python2.7/site-packages/flask/app.py", line 62, in wrapper_func
return f(self, *args, **kwargs)
File "/home/chenhj/.virtualenvs/multiapp/local/lib/python2.7/site-packages/flask/app.py", line 885, in register_blueprint
(blueprint, self.blueprints[blueprint.name], blueprint.name)
AssertionError: A blueprint's name collision occurred between <flask.blueprints.Blueprint object at 0x25e5d90> and <flask.blueprints.Blueprint object at 0x21b89d0>. Both share the same name "user". Blueprints that are created on the fly need unique names.
i am crazy about that
If you have a blueprint that has a name of users already then your 'admin' blueprint for your admin users model view needs to be called something different.
You can achieve this with the endpoint var in ModelView
Flask-Admin - ModelView
admin.add_view(ModelView(Users, db.session, endpoint="users_"))
The collision is because you have a module name user and a blueprint called user. Rename the blueprint to user_blueprint. From the code it seems you have a folder called user, a module called user and a blueprint called user. You can avoid problems later on with some descriptive names. Otherwise it is just plain confusing.
You can also override the admin blueprint names in a ModelView subclass:
from flask_admin.contrib.sqla import ModelView
class AppModelView(ModelView):
def create_blueprint(self, admin):
blueprint = super(AppModelView, self).create_blueprint(admin)
blueprint.name = '{}_admin'.format(blueprint.name)
return blueprint
def get_url(self, endpoint, **kwargs):
if not (endpoint.startswith('.') or endpoint.startswith('admin.')):
endpoint = endpoint.replace('.', '_admin.')
return super(AppModelView, self).get_url(endpoint, **kwargs)

SQLAlchemy create_all() does not create tables

I'm trying to integrate PostgreSQL and SQLAlchemy but SQLAlchemy.create_all() is not creating any tables from my models.
My code:
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://login:pass#localhost/flask_app'
db = SQLAlchemy(app)
db.create_all()
db.session.commit()
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
admin = User('admin', 'admin#example.com')
guest = User('guest', 'guest#example.com')
db.session.add(admin)
db.session.add(guest)
db.session.commit()
users = User.query.all()
print users
But I get this error: sqlalchemy.exc.ProgrammingError: (ProgrammingError) relation "user" does not exist
How can I fix this?
You should put your model class before create_all() call, like this:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://login:pass#localhost/flask_app'
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
with app.app_context():
db.create_all()
db.session.add(User('admin', 'admin#example.com'))
db.session.add(User('guest', 'guest#example.com'))
db.session.commit()
users = User.query.all()
print(users)
If your models are declared in a separate module, import them before calling create_all().
Say, the User model is in a file called models.py,
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://login:pass#localhost/flask_app'
db = SQLAlchemy(app)
# See important note below
from models import User
with app.app_context():
db.create_all()
db.session.add(User('admin', 'admin#example.com'))
db.session.add(User('guest', 'guest#example.com'))
db.session.commit()
users = User.query.all()
print(users)
Important note: It is important that you import your models after initializing the db object since, in your models.py you also need to import the db object from this module.
If someone is having issues with creating tables by using files dedicated to each model, be aware of running the "create_all" function from a file different from the one where that function is declared.
So, if the filesystem is like this:
Root
--app.py <-- file from which app will be run
--models
----user.py <-- file with "User" model
----order.py <-- file with "Order" model
----database.py <-- file with database and "create_all" function declaration
Be careful about calling the "create_all" function from app.py.
This concept is explained better by the answer to this thread posted by #SuperShoot
This is probably not the main reason why the create_all() method call doesn't work for people, but for me, the cobbled together instructions from various tutorials have it such that I was creating my db in a request context, meaning I have something like:
# lib/db.py
from flask import g, current_app
from flask_sqlalchemy import SQLAlchemy
def get_db():
if 'db' not in g:
g.db = SQLAlchemy(current_app)
return g.db
I also have a separate cli command that also does the create_all:
# tasks/db.py
from lib.db import get_db
#current_app.cli.command('init-db')
def init_db():
db = get_db()
db.create_all()
I also am using a application factory.
When the cli command is run, a new app context is used, which means a new db is used. Furthermore, in this world, an import model in the init_db method does not do anything, because it may be that your model file was already loaded(and associated with a separate db).
The fix that I came around to was to make sure that the db was a single global reference:
# lib/db.py
from flask import g, current_app
from flask_sqlalchemy import SQLAlchemy
db = None
def get_db():
global db
if not db:
db = SQLAlchemy(current_app)
return db
I have not dug deep enough into flask, sqlalchemy, or flask-sqlalchemy to understand if this means that requests to the db from multiple threads are safe, but if you're reading this you're likely stuck in the baby stages of understanding these concepts too.

Categories

Resources