Sqlalchemy add duplicated record in database - python

I'm learning the way how to modularize flask application using blueprints and break database models into multiple files (one per entity)
I encounter a strange issue while building SQLite file and writing initial record for admin account, - SQLAlchemy added two records for one user.
Here is project layout and key files
runserver.py
#import os
from sbsuite import app
from sbsuite.database import init_db, createAdmin
init_db()
createAdmin()
if __name__ == "__main__":
app.run(debug=True)
models.py
from sbsuite import app
from sqlalchemy import Column, Integer, String
from sbsuite.database import Base
from flask_marshmallow import Marshmallow
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(50))
email = Column(String(80))
password = Column(String(50))
roles = Column(String(50))
# JSON Schema
# should I create Marshmallow instance here?
ma = Marshmallow(app)
class UserSchema(ma.Schema):
class Meta:
fields = ('id', 'name', 'email', 'password')
user_schema = UserSchema()
users_schema = UserSchema(many=True)
database.py
from sbsuite import app
from sqlalchemy import create_engine
from sqlalchemy.orm import Session, scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from flask_sqlalchemy import SQLAlchemy
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()
from sbsuite.api.models import User, UserSchema
from sbsuite.api.productMdl import Product, ProductSchema
def init_db():
Base.metadata.create_all(bind=engine)
def createAdmin():
print("Create admin user")
admin = User(name="admin",email="admin#sbsuite.com", password="password", roles = "admin")
with Session(engine) as session:
session.add(admin)
session.commit()
print("admin user created") # why users added twice?
In last snippet after session.commit() I check database and see two records for admin user.

Related

How to create db table with SQLite / SQLAlchemy?

I'm trying to create a table in an existing db with SQLAlchemy/SQLite where I have to store a user and password, but it returns an error saying the column pwd doesn't exist!
Am I missing something? Am I messing it up?
I still didn't quite understand, I followed all steps in some online tutorial but nothing still.
Here is the class object that I developed, then from another register form I try to store the pw from the application, but the error should be here in this code:
import os
basedir = os.path.abspath(os.path.dirname(__file__))
# flask imports
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from sqlalchemy import *
# create flask app
app = Flask(__name__)
# set sqllite db connection
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')
engine= create_engine(app.config['SQLALCHEMY_DATABASE_URI'])
# bcrypt extension init to app
bcrypt = Bcrypt(app)
# sqlite init to app
db = SQLAlchemy(app)
# define meta data for table
meta=MetaData()
userstable = Table('users', meta, Column('id', Integer, primary_key = True, autoincrement=True), Column('username', String, unique=True), Column('pwd', String))
# create table to sqlite
meta.create_all(engine)
class User(db.Model):
# user columns
id = db.Column(db.Integer(), primary_key=True, autoincrement=True)
username = db.Column(db.String(64), unique=True)
pwd = db.Column(db.String(128))
def __init__(self,username,pwd):
#self.id=id
self.username=username
self.pwd=bcrypt.generate_password_hash(pwd)
# create user object
user = User('anish','23434')
# insert user object to sqlite
db.session.add(user)
# commit transaction
db.session.commit()
This is the error:
sqlalchemy.exc.OperationalError OperationalError: (sqlite3.OperationalError) table user has no column named pwd [SQL: INSERT INTO user (username, pwd) VALUES (?, ?)] [parameters: ('ita_itf', '$2b$12$VmpTsd0o4uTLj0wGypGu7ujhzYHLlV8k9ekaIP1.yh5lUMMgOM4MC')]
import os
basedir = os.path.abspath(os.path.dirname(__file__))
# flask imports
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from sqlalchemy import *
# create flask app
app = Flask(__name__)
# set sqllite db connection
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')
engine= create_engine(app.config['SQLALCHEMY_DATABASE_URI'])
# bcrypt extension init to app
bcrypt = Bcrypt(app)
# sqlite init to app
db = SQLAlchemy(app)
# define meta data for table
meta=MetaData()
userstable = Table('users', meta, Column('id', Integer, primary_key = True, autoincrement=True), Column('username', String, unique=True), Column('pwd', String))
# create table to sqlite
meta.create_all(engine)
class User(db.Model):
# table name for User model
__tablename__ = "users"
# user columns
id = db.Column(db.Integer(), primary_key=True, autoincrement=True)
username = db.Column(db.String(64), unique=True)
pwd = db.Column(db.String(128))
def __init__(self,username,pwd):
#self.id=id
self.username=username
self.pwd=bcrypt.generate_password_hash(pwd)
# create user object
user = User('anish','23434')
# insert user object to sqlite
db.session.add(user)
# commit transaction
db.session.commit()

Importing SQLAlchemy models used in relationship?

I'm new to SQLAlchemy (using Python 3) and find the following puzzling. In my simple example, there are 2 model classes defined in separate files with a relationship linking them.
Is the setup correct? My code requires that Animal.py import Owner because a relationship is defined, otherwise app/main.py will throw an error about Owner class not found. However, the official docs and other online examples do not appear to import the other classes that the current class has a relationship with.
Will having model/__init__.py be useful for my case? If so, what will it be used for? Saw an example that used a __init__.py file.
Github Repo: https://github.com/nyxynyx/sqlalchemy-class-import-error
File Structure
app/main.py
import sys
sys.path.append('..')
from lib.db import db_session
from models.foo.Animal import Animal
if __name__ == '__main__':
print(Animal.query.all())
models/foo/Animal.py
from sqlalchemy import *
from sqlalchemy.orm import relationship
from ..Base import Base
from .Owner import Owner <-------------- !!!!! if not imported, error occurs when running main.py !!!!!
class Animal(Base):
__tablename__ = 'animals'
id = Column(Integer, primary_key=True)
name = Column(Text)
owner_id = Column(Integer, ForeignKey('owners.id'))
owner = relationship('Owner')
models/Foo/Owner.py
from sqlalchemy import *
from ..Base import Base
class Owner(Base):
__tablename__ = 'owners'
id = Column(Integer, primary_key=True)
name = Column(Text)
lib/db.py
import json
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy import create_engine
with open('../settings.json') as f:
settings = json.load(f)
user, password, host, port, dbname = settings['db']['user'], settings['db']['password'], settings['db']['host'], settings['db']['port'], settings['db']['dbname']
connection_url = f'postgresql://{user}:{password}#{host}:{port}/{dbname}'
engine = create_engine(connection_url)
Session = sessionmaker(autocommit=False, autoflush=False, bind=engine)
db_session = scoped_session(Session)
The animal.py is fine. The issue is that if owner.py is never imported, sqlalchemy never sees the Owner model/table so it never registers it into the Base metadata. You can remove the import of Owner from animal.py into your main.py as
import models.foo.Owner
to see it work while keeping the separate model files.

Flask python app.py doesn't run but manage.py runserver does [duplicate]

This question already has an answer here:
Tyring to set up modelview with Flask-Admin causes ImportError
(1 answer)
Closed 5 years ago.
I have a flask app which I am trying to run. It seems when I run python app.py, there is a circular import of some sort. Here is the code for both my models.py and app.py:
models.py
import datetime
from app import bcrypt, db
class User(BaseModel, db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
email = db.Column(db.String(255), unique=True, nullable=False)
password = db.Column(db.String(255), nullable=False)
registered_on = db.Column(db.DateTime, nullable=False)
admin = db.Column(db.Boolean, nullable=False, default=False)
def __init__(self, email, password, admin=False):
self.email = email
self.password = bcrypt.generate_password_hash(password)
self.registered_on = datetime.datetime.now()
self.admin = admin
app.py
from flask import Flask, render_template
from flask import request, jsonify, session
from flask.ext.bcrypt import Bcrypt
from flask_sqlalchemy import SQLAlchemy
from models import User
app = Flask(__name__)
db = SQLAlchemy()
POSTGRES = {
'user': 'postgres',
'db': 'postgres',
'host': 'localhost',
'port': '5432',
}
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://%(user)s#%(host)s:%(port)s/%(db)s' % POSTGRES
app.config['SECRET_KEY'] = 'lol'
bcrypt = Bcrypt(app)
db.init_app(app)
#app.route('/api/register', methods=['POST'])
def register():
json_data = request.json
user = User(
email=json_data['email'],
password=json_data['password']
)
try:
db.session.add(user)
db.session.commit()
status = 'success'
except:
status = 'this user is already registered'
db.session.close()
return jsonify({'result': status})
manage.py
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from app import app, db
from models import User
migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command('db', MigrateCommand)
#manager.command
def create_admin():
"""Creates the admin user."""
db.session.add(User(email='admin#admin.com', password='admin', admin=True))
db.session.commit()
if __name__ == '__main__':
manager.run()
I have played around with the imports but nothing seems to work, I usually will get a circular import error like this:
Traceback (most recent call last):
File "app.py", line 7, in <module>
from models import User
File "/Users/Rishub/Desktop/apps/topten/models.py", line 3, in <module>
from app import bcrypt, db
File "/Users/Rishub/Desktop/apps/topten/app.py", line 7, in <module>
from models import User
ImportError: cannot import name User
Does anyone know the solution to this?
In addition, if I run python manage.py runserver, my app runs fine so I am curious as to why this works but python app.py does not.
in models.py you are importing:
from app import bcrypt, db
in app.py you are importing models.
from models import User
To fix it restructure the program as shown here: http://flask.pocoo.org/docs/0.12/patterns/sqlalchemy/
create a database.py file with database configs etc.
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine('sqlite:////tmp/test.db', 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 models
Base.metadata.create_all(bind=engine)
use this in models.py as
from database import Base
from sqlalchemy import Column, Integer, String
# instead of db.column directly use Column
class User(Base):
and app files as:
from database import init_db
init_db()
from database import db_session
from models import User
#app.route('/api/register', methods=['POST'])
def register():
json_data = request.json
user = User(
email=json_data['email'],
password=json_data['password']
)
db_session.add(user)
db_session.commit()
return jsonify({'result': 'success'})

How to create a single table using SqlAlchemy declarative_base

To create the User table I have to use drop_all and then create_all methods. But these two functions re-initiate an entire database. I wonder if there is a way to create the User table without erasing (or dropping) any existing tables in a database?
import sqlalchemy
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True)
name = sqlalchemy.Column(sqlalchemy.String)
def __init__(self, code=None, *args, **kwargs):
self.name = name
url = 'postgresql+psycopg2://user:pass#01.02.03.04/my_db'
engine = sqlalchemy.create_engine(url)
session = sqlalchemy.orm.scoped_session(sqlalchemy.orm.sessionmaker())
session.configure(bind=engine, autoflush=False, expire_on_commit=False)
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
You can create/drop individual tables:
User.__table__.drop(engine)
User.__table__.create(engine)
from app import db
from models import User
User.__table__.create(db.engine)
User.__table__.drop(db.engine)
Another way to accomplish the task:
Base.metadata.tables['users'].create(engine)
Base.metadata.tables['users'].drop(engine)

creating tables from different models with sqlalchemy

I have different models e.g. model1.py, model2.py etc. Some how tables are being created following pocoo link, which required to be invoked from terminal.
But
def init_db():
import model.model1
import model.model2
Base.metadata.create_all(bind=engine)
This is not working, rather requires to be invoked from terminal.
>> from database import init_db
>> init_db() #works
database.py
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine('sqlite:///xyz.sqlite', echo=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 model.admin # from model.admin import User doesnt help either
import model.role
Base.metadata.create_all(bind=engine)
if __name__ == '__main__':
init_db()
admin.py
from sqlalchemy import Column, Integer, String
from database import Base
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(50), unique=True)
email = Column(String(120), unique=True)
def __init__(self, name=None, email=None):
self.name = name
self.email = email
def __repr__(self):
return '<User %r>' % (self.name)
There are no errors although an empty db file is generated.
How can database be created from multiple models?
I'm not sure why invoking directly from command line triggers table creation for you but I've always structured my Flask apps ala Digital Ocean's guide. Something that wasn't noted explicitly in the quide is the fact that you need to initialize your blueprints first before create_all is able to build the database tables for you.
(Your code as it is, lacks blueprints. Maybe try to create some first then try again?)

Categories

Resources