sqlalchemy db.create_all() creates unexpected tables - python

I tried adding a second table to my website database. First one works and is where I collect inputted e-mails. I wanted a second table that connects inputted instagram handles. So I made this code for my app.py file:
from flask import Flask, jsonify, request, send_from_directory
from flask_sqlalchemy import SQLAlchemy
import datetime
from flask_marshmallow import Marshmallow
from flask_cors import CORS
app = Flask(__name__, static_url_path='', static_folder='frontend/build')
CORS(app)
app.config['SQLALCHEMY_DATABASE_URI']='mysql://***:***#localhost/pobble_data'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS']= False
db = SQLAlchemy(app)
ma = Marshmallow(app)
class Emails(db.Model):
id = db.Column(db.Integer, primary_key = True)
email = db.Column(db.String(120))
def __init__(self, email):
self.email = email
class EmailsSchema(ma.Schema):
class Meta:
fields = ('id','email')
class IGHandles(db.Model):
id = db.Column(db.Integer, primary_key = True)
ighandle = db.Column(db.String(120))
def __init__(self, ighandle):
self.ighandle = ighandle
class IGHandlesSchema(ma.Schema):
class Meta:
fields = ('id','ighandles')
email_schema = EmailsSchema()
emails_schema = EmailsSchema(many=True)
ighandle_schema = IGHandlesSchema()
ighandles_schema = IGHandlesSchema(many=True)
#app.route("/", defaults={'path':''})
def serve(path):
return send_from_directory(app.static_folder, 'index.html')
#app.route('/get', methods = ['GET'])
def get_emails():
all_emails = Emails.query.all()
results = emails_schema.dump(all_emails)
return jsonify(results)
def get_ighandles():
all_ighandles = IGHandles.query.all()
results = ighandles_schema.dump(all_ighandles)
#app.route('/get/<id>', methods = ['GET'])
def post_emails(id):
results = Emails.query.get(id)
return email_schema.jsonify(results)
def post_ighandles(id):
results = IGHandles.query.get(id)
return ighandle_schema.jsonify(results)
#app.route('/add', methods = ['POST'])
def add_email2():
email = request.json['email']
entry = Emails(email)
db.session.add(entry)
db.session.commit()
return email_schema.jsonify(entry)
def add_ighandles():
ighandle = request.json['ighandle']
entry = IGHandles(ighandle)
db.session.add(entry)
db.session.commit()
if __name__ == "__main__":
app.run(debug=True)
I saved all of this and went to my python interpreter and ran
from app import db and
db.create_all()
and I get two tables: emails and ig_handles
I expected the following two tables because they are the class names: Emails and IGHandles.
In fact I searched all of my code and don't see ig_handles anywhere so am not sure where it gets that from. I think it's some sort of cached name I might have used when I was experimenting with making the tables earlier.
I've tried running db.session.expire_all() and that doesn't help.

Related

SQLAlchemy warning: column won't be part of the declarative mapping

trying to build to-do list app with python , flask and sqlalchemy;
when creating a table to database i got this error
i run this command
'from app import db'
I receive this warning :
C:\Users\Mahmoud\.virtualenvs\to-do_list_flask-Gr0Wv-V5\lib\site-packages\sqlalchemy\ext\declarative\base.py:361: SAWarning: Attribute 'item' on class <class 'app.todo'> appears to be a non-schema 'sqlalchemy.sql.column()' object; this won't be part of the declarative mapping
% (key, cls)
C:\Users\Mahmoud\.virtualenvs\to-do_list_flask-Gr0Wv-V5\lib\site-packages\sqlalchemy\ext\declarative\base.py:361: SAWarning: Attribute 'date' on class <class 'app.todo'> appears to be a non-schema 'sqlalchemy.sql.column()' object; this won't be part of the declarative mapping
% (key, cls)
if i do excute db.create_all() the table only has an id column
my script:
from flask import Flask, render_template, request
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgres://postgres:123456#localhost/todo_list'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
class todo(db.Model):
__tablename__ = 'todo'
id = db.Column(db.Integer, primary_key=True)
item = db.column(db.String(500))
date = db.column(db.Date)
def __init__(self, item, date):
self.item = item
self.date = date
#app.route('/')
def index():
return render_template('index.html')
#app.route('/submit', methods = ['POST'])
def submit():
if request.method == 'POST':
item = request.form['item']
date = request.form['date']
data = todo(item, date)
db.session.add(data)
db.session.commit()
return render_template('index.html', item = item, date = date)
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8000, debug=True)
I was having this same error. Turns out its just a litte syntax error. Column should be upcase so:
id = db.Column(db.Integer, primary_key=True)
item = db.Column(db.String(500))
date = db.Column(db.Date)
Should fix it.

Error deseralizing JSON string with nested objects with SqlAlchemy and Marshmallow

I'm trying to build a REST API application in Python 3.4, using Flask, SQLAlchemy and Marshmallow.
In my model I have a User class with a one to many relationship with a MailAddress class.
If I run a GET request, I manage to read data from the DB, and the data is correctly returned as a JSON string.
Instead, if I run a POST request with the JSON serialization of my User object with some MailAddresses, I get this error:
File "X:\test\...\site-packages\sqlalchemy\orm\collections.py", line 785, in bulk_replace
constants = existing_idset.intersection(values or ())
File "X:\test\...\site-packages\sqlalchemy\util\_collections.py", line 612, in intersection
result._members.update(self._working_set(members).intersection(other))
TypeError: unhashable type: 'dict'
I've tried adding a __hash__ function to my model classes (as suggested at sqlalchemy: TypeError: unhashable type creating instance, sqlalchemy) but that didn't help.
Here is a complete code example that shows this problem:
from flask import Flask, request
from flask_marshmallow import Marshmallow
from flask_sqlalchemy import SQLAlchemy
from marshmallow import fields
from sqlalchemy import Table, Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
class Config(object):
SQLALCHEMY_DATABASE_URI = '<CONNECTION STRING HERE>'
SQLALCHEMY_TRACK_MODIFICATIONS = False
app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
ma = Marshmallow(app)
# Model
class MailAddress(db.Model):
__tablename__ = 'mail_addresses'
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('users.id'))
mail_type = Column(String(200), nullable=False)
mail = Column(String(200), nullable=False)
def __init__(self, mail, mail_type):
self.mail = mail
self.mail_type = mail_type
class MailAddressSchema(ma.ModelSchema):
class Meta:
model = MailAddress
class User(db.Model):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(200), nullable=False)
mail_addresses = relationship('MailAddress', backref='user')
def __init__(self, name, mail_addresses):
self.name = name
self.mail_addresses = mail_addresses
def __hash__(self):
return hash(self.name)
class UserSchema(ma.ModelSchema):
mail_addresses = fields.Nested(MailAddressSchema, many = True, only=('mail', 'mail_type'))
class Meta:
model = User
# Routes
user_schema = UserSchema()
#app.route('/api/v0/user', methods=['GET'])
def user_get():
users = db.session.query(User).all()
return user_schema.jsonify(users, many = True), 200
#app.route('/api/v0/user', methods=['POST'])
def user_create():
new_instance = user_schema.make_instance(request.json)
db.session.add(new_instance)
db.session.commit()
return user_schema.jsonify(new_instance), 201
# Main
if __name__ == '__main__':
app.run('localhost', 5555)
Is there something I'm missing?
Use load instead make_instance
#app.route('/api/v0/user', methods=['POST'])
def user_create():
new_instance, errors = user_schema.load(request.json)
db.session.add(new_instance)
db.session.commit()
return user_schema.jsonify(new_instance), 201

Update not commited on mysql server flask-sqlalchemy

I am able to perform 'select' queries to my Mysql database.
However, the "insert" ones don't change the database, only the python objects. So when I restart the flask app, all the commited(?) editions are gone.
Views:
from flask import Flask, render_template, request, redirect, url_for, flash, Response
from sqlalchemy import exc
from models import *
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = 'kjhS7usfHGJHDez78'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+mysqldb://admin:admin#127.0.0.1:3306/grenier'
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)
db.create_all()
#app.route('/ajax/submit_edition', methods=['POST'])
def submit_edition():
if request.method == 'POST':
given_id=1
show = Shows.query.filter_by(id=given_id).first()
show.short_description = "Hello"
try:
db.session.commit()
db.session.flush()
return "ok"
except exc.SQLAlchemyError:
return "Error in commiting the edition"
No particular exception is found. The route always returns "ok".
Models:
from sqlalchemy import Column, ForeignKey
from sqlalchemy.dialects.mysql import LONGTEXT, YEAR
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Shows(db.Model):
__tablename__ = "shows"
id = Column(db.Integer, ForeignKey("programmation.id"), primary_key=True)
date = Column(db.DATETIME)
title = Column(db.VARCHAR(50))
short_description = Column(db.VARCHAR(200))
type = Column(db.VARCHAR(20))
background_image = Column(db.VARCHAR(150))
content = Column(LONGTEXT)
def serialize(self, whatTo):
result = {}
if 'id' in whatTo:
result['id'] = self.id
if 'date' in whatTo:
result['date'] = str(self.date)
if 'title' in whatTo:
result['title'] = self.title
if 'short_description' in whatTo:
result['short_description'] = self.short_description
if 'type' in whatTo:
result['type'] = self.type
if 'background_image' in whatTo:
result['background_image'] = self.background_image
if 'content' in whatTo:
result['content'] = self.content
return result
class Programmation(db.Model):
__tablename__ = "programmation"
id = Column(db.Integer, primary_key=True)
semester = Column(db.Integer)
year = Column(YEAR)
When I look at the logs, the sql request is created for the select. But for the insert commit(), there is nothing.
Thank you !
The problem is usage of two different SQLAlchemy instance. When you call db.create_all() method it creates all tables which which inherited from db.Model but in your views you don't have any model inherited from db = SQLAlchemy(app). All your models inherited from other SQLAlchemy instance. To fix this import the db object from views to models module and use it as parent class for inheritance:
#models.py
from views import db
#db = SQLAlchemy() #remove this line
class Show(db.Model):
...

sqlalchemy insert data does not work

In models.py I have define:
class slidephoto(db.Model):
__tablename__ = 'slide_photo'
id = db.Column(db.Integer, primary_key=True)
uid = db.Column(db.Integer, nullable=False)
photo = db.Column(db.String(collation='utf8_bin'), nullable=False)
def __init__(self, uid, photo):
self.uid = uid
self.photo = photo
def __repr__(self):
return "{'photo': " + str(self.photo) + "}"
I select data like this (for example):
#app.route('/index/')
def index():
user_photo = slidephoto.query.filter_by(uid=5).all()
Now I want to know how to insert data. I tried this:
#app.route('/insert/')
def insert():
act = slidephoto.query.insert().execute(uid='2016', photo='niloofar.jpg')
return 'done'
But it does not do what I need. What should I do?
I have read and tested other answers and solutions, but none of them was useful for my script.
================ update ================
I don't no if it helps... but here is all imports and configs in app.py:
import os, sys
from niloofar import *
from flask import Flask, request, url_for, render_template, make_response, redirect
from flask_sqlalchemy import SQLAlchemy
from werkzeug.utils import secure_filename
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://myusername:mypassword#localhost/mydbname'
db = SQLAlchemy(app)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
I hope that my answer will help you solving the problem.
from sqlalchemy import create_engine, MetaData, Table, insert
# I have tested this using my local postgres db.
engine = create_engine('postgresql://localhost/db', convert_unicode=True)
metadata = MetaData(bind=engine)
con = engine.connect()
act = insert(slidephoto).values(uid='2016', photo='niloofer.jpg')
con.execute(act)
I write a simple demo that do insert work, you can take it as a reference:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class FirstTest(db.Model):
__tablename__ = "first_test"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False)
def __init__(self, name):
self.name = name
# Fill your db info here
mysql_info = {
"user": "",
"pwd": "",
"host": "",
"port": 3306,
"db": "",
}
app = Flask(__name__)
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True
# Here I use pymysql
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://{0}:{1}#{2}:{3}/{4}".format(
mysql_info["user"], mysql_info["pwd"], mysql_info["host"],
mysql_info["port"], mysql_info["db"])
db.__init__(app)
#app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
with app.test_request_context("/"):
record = FirstTest("test")
db.session.add(record)
db.session.commit()
You should use query as a method. Like 'query()'

SQLAlchemy Object already attached to session

I'm trying to get a server for an app working, but I'm getting an error upon login:
[!] Object '<User at 0x7f12bc185a90>' is already attached to session '2' (this is '3')
It seems the session I'm adding is already on the database. This is the snippet of code that is causing the problem:
#app.route('/login', methods=['POST'])
def login():
u = User.query.filter(User.username == request.form["username"]).first()
if not u or u.password != request.form["password"]:
return error("E1")
s = Session.get_by_user(u)
if s is not None:
db_session.delete(s)
db_session.commit()
print db_session.execute("SELECT * FROM sessions").fetchall()
s = Session(u)
db_session.add(s)
db_session.commit()
return jsonify(s.values)
As you can see, I'm printing the content from the sessions table before trying to add anything, and it is empty! ([])
What else could be causing this?
Here is the 'Session' implementation:
class Session(Base):
__tablename__ = "sessions"
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('users.id'), unique=True)
user = relationship(User)
key = Column(String(50), unique=True)
created = Column(DateTime)
def __init__(self, user=None):
self.user = user
self.key = base64.encodestring(os.urandom(24)).strip()
self.created = datetime.now()
def __repr__(self):
return '<Session %r>' % (self.key)
#property
def values(self):
return {"username" : self.user.username,
"key" : self.key,
"created" : str(self.created),
}
#classmethod
def get_by_key(cls, key):
s = cls.query.filter(cls.key == key).first()
#print datetime.now() - s.created
if s and datetime.now() - s.created > settings.SESSION_LIFETIME:
s = None
return s
#classmethod
def get_by_user(cls, user):
s = cls.query.filter(cls.user == user).first()
if s and datetime.now() - s.created > settings.SESSION_LIFETIME:
s.query.delete()
db_session.commit()
s = None
return s
As #marcinkuzminski mentioned, you can't add an object that is already attached to another session. Just pulling in the original session from the object with object_session() is risky, though, if you aren't sure that session originated in the same thread context you're currently operating in. A thread-safe method is to use merge():
local_object = db_session.merge(original_object)
db_session.add(local_object)
db_session.commit()
Object you're trying to modify is already attached to another session.
Maybe you have wrong imports, and db_session is a new instance.
A good workaround to this is to extract the current bound session and use it:
Instead of:
db_session.add(s)
Do:
current_db_sessions = db_session.object_session(s)
current_db_sessions.add(s)
This db session issue will arise if you are having server.py and model.py importing each other
server.py
from flask import Flask
import os
import models as appmod #################### importing models here in server.py<----------
app = Flask(__name__) # L1
app.config.from_object(os.environ['APP_SETTINGS']) # L2
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # L3
database = SQLAlchemy(app) # L4
db = database # L5
#app.route('/item_delete/<id>', methods=['DELETE'])
def remove_method(id = None):
data_rec = appmod.Employee.query.get(id)
db.session.delete(data_rec)
db.session.commit()
return "DELETE"
if __name__ == '__main__':
app.run(port=5000, host='0.0.0.0',debug=True,threaded=True)
models.py
from server import db #################### importing server in models.py here <------------
from sqlalchemy.dialects.mysql import JSON
class Employee(db.Model):
__tablename__ = 'employe_flask'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128))
datetime = db.Column(db.DateTime)
designation = db.Column(db.String(128))
def __init__(self, name, datetime, designation):
self.name = name
self.datetime = datetime
self.designation = designation
#staticmethod
def delete_rec(data_rec):
db.session.delete(data_rec)#.delete
db.session.commit()
def __repr__(self):
record = {"name":self.name,"date":self.datetime.ctime(),"designation":self.designation}.__str__()
return record
Remove the line L1 to L5 from server.py and place it in common file like settings.py
and import 'app' and 'db' to server.py and import db in models.py
like this files below
server.py
from flask import Flask
import os
import models as appmod
from settings import app, db
#app.route('/item_delete/<id>', methods=['DELETE'])
def remove_method(id = None):
data_rec = appmod.Employee.query.get(id)
db.session.delete(data_rec)
db.session.commit()
return "DELETE"
if __name__ == '__main__':
app.run(port=5000, host='0.0.0.0',debug=True,threaded=True)
settings.py
import os
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__) # L1
app.config.from_object(os.environ['APP_SETTINGS']) # L2
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # L3
database = SQLAlchemy(app) # L4
db = database # L5
models.py
from settings import db
from sqlalchemy.dialects.mysql import JSON
class Employee(db.Model):
__tablename__ = 'employe_flask'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128))
datetime = db.Column(db.DateTime)
designation = db.Column(db.String(128))
def __init__(self, name, datetime, designation):
self.name = name
self.datetime = datetime
self.designation = designation
#staticmethod
def delete_rec(data_rec):
db.session.delete(data_rec)#.delete
db.session.commit()
def __repr__(self):
record = {"name":self.name,"date":self.datetime.ctime(),"designation":self.designation}.__str__()
return record
This error means the record you are handling is attached to 2 different session(db)!
One of the reasons is that you may define your model with one db = SQLAlchemy(app) and add/insert/modify the database with another!
My solution is UNIFORMING THE DB!
try this:
u = db.session.query(User).filter(User.username == request.form["username"]).first()
Instead of this:
u = User.query.filter(User.username == request.form["username"]).first()
I had this problem too.
I created a test_file.py and added this code:
from app import app
from models import Tovar
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy(app)
tovardel = Tovar.query.filter(Tovar.region == 1 and Tovar.price == 12).first()
db.session.delete(tovardel)
tovar = Tovar.query.filter(Tovar.region == 1 and Tovar.price == 12).first()
print(tovar.description)
and when I ran the code I got this error:
Object '<Tovar at 0x7f09cbf74208>' is already attached to session '1' (this is '2')
PROBLEM SOLVING:
If you have db = SQLAlchemy(app) in, for example, text_file.py, and in app.py, you get this problem all time. You should del db = SQLAlchemy(app), and import db from app from app import db
I faced the same issue. I was defining models in a separate file and I had to call SQLAlchemy twice. That's why there were two different sessions were running.
I solved this by doing following:
In case you are trying to remove an object from db:
Just create the removeObject function inside the model

Categories