I have problems operating with the exiting database in mysql using a sqlalchemy as I need it for building Flask RESTful api.
I get funny errors about circular dependencies here:
from flask import Flask, request, jsonify, make_response
from flaskext.mysql import MySQL
from flask_sqlalchemy import SQLAlchemy
#from webapi import models
# mysql = MySQL()
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secretkey' #for use later
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://user:password#127.0.0.1/mydb'
db = SQLAlchemy(app)
class Extensions(db.Model):
__tablename__ = "Extensions"
extnbr = db.Column(db.Integer, primary_key=True, default="NULL")
authname = db.Column(db.String(256), nullable=True, default="#nobody")
extpriority = db.Column(db.Integer, nullable=True, default=0)
callaccess = db.Column(db.Integer, nullable=True, default=0)
extlocation = db.Column(db.String, nullable=True, default="NULL")
display = db.Column(db.String, nullable=True, default="NULL")
#app.route("/")
def hello():
return "Welcome to Python Flask App!"
#app.route("/extensions", methods=["GET"])
def get_all_extensions():
users = Extensions.query.all()
output = []
for user in users:
user_data = {}
user_data['extnbr'] = user.extnbr
user_data['authname'] = user.authname
user_data['extpriority'] = user.extpriority
user_data['callaccess'] = user.callaccess
user_data['extlocation'] = user.extlocation
user_data['display'] = user.display
output.append({'extensions' : output})
return jsonify({'users' : output})
if __name__ == "__main__":
app.run(debug=True)
I get error about circular dependence:
Debug and trace:
https://dpaste.de/F3uX
This has to do with your output. You're appending a dictionary containing output to output itself, creating a funky data structure causing the json error.
Try changing
output.append({'extensions' : output})
to this:
output.append({'extensions' : user_data })
(Which is what I assume you want anyways)
The problem is:
output = []
for user in users:
...
output.append({'extensions' : output}) # here you are adding output to the output
check this example:
out = []
for i in range(2):
out.append({'ext': out})
# output
[{'ext': [...]}, {'ext': [...]}]
You have the same output. I am sure this is not what you want. So change the line with problem to this one: output.append({'extensions' : user_data})
Related
This question has been asked a few times I never been able to fix my problem since a few days. I tried to create a new object in my sqllite db related to a Model that I created but I always have this issue:
Arguments: (OperationalError('(sqlite3.OperationalError) no such table: sku_model'),)
app.py
import requests
from flask import Flask
from flask_crontab import Crontab
from app.routes import routes_blueprint
from app.config import BaseConfig, BasicConfig, TestConfig
import os
from statistics import median
import click
from app.models import SKUModel, db
def create_app():
app = Flask(__name__)
app.config["SECRET_KEY"] = "any secret key"
app.config["SQLALCHEMY_DATABASE_URI"] = BaseConfig.SQLALCHEMY_DATABASE_URI
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db.init_app(app)
app.register_blueprint(routes_blueprint)
crontab.init_app(app)
return app
def setup_database(app):
with app.app_context():
db.create_all()
app.logger.info("DB init!")
crontab = Crontab()
if __name__ == "__main__":
app = create_app()
# not os.path.isfile(BaseConfig.SQLALCHEMY_DATABASE_URI):
setup_database(app)
app.run()
database.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
models.py
import flask_sqlalchemy
db = flask_sqlalchemy.SQLAlchemy()
class SKUModel(db.Model):
id = db.Column(db.Integer, primary_key=True)
sku = db.Column(db.String)
product_title = db.Column(db.String)
quantity = db.Column(db.Integer)
price = db.Column(db.Float)
# JSON serializer
def to_json(self):
return {
"id": self.id,
"sku": self.sku,
"product_title": self.product_title,
"quantity": self.quantity,
"price": self.price,
}
config.py
import os
# default config
class BaseConfig(object):
DEBUG = False
# shortened for readability
SECRET_KEY = '\xbf\xb0\x11\xb1\xcd\xf9\xba\x8bp\x0c...'
SQLALCHEMY_PATH = "/tmp/database.db"
#SQLALCHEMY_DATABASE_URI = "sqlite:////tmp/sku.db"
SQLALCHEMY_DATABASE_URI = "sqlite:///" + SQLALCHEMY_PATH
class TestConfig(BaseConfig):
DEBUG = True
TESTING = True
WTF_CSRF_ENABLED = False
SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
class BasicConfig(BaseConfig):
DATASET_PATH = "app/data/dataset.json"
routes.py
from itertools import product
import json
from statistics import median
import requests
from flask import Blueprint, request
from app.models import SKUModel, db
from app.core import get, get_all, update_dataset, get_5_highest, update_21, create, delete, get_lowest
routes_blueprint = Blueprint("route_blueprint", __name__)
#routes_blueprint.route("/")
def hello():
return "Hello World!"
# Retrieve the current timezone using the WorldTimeAPI (http://worldtimeapi.org) for any country available in the service
#routes_blueprint.route("/timezone/<string:area>/<string:region>")
def timezone(area, region):
url = f"http://worldtimeapi.org/api/timezone/{area}/{region}"
response = requests.get(url)
return (
{"UTF:": response.json()["utc_offset"]}
if response.ok
else {"response error": response.text, "http code": response.status_code}
)
# Get one SKU
#routes_blueprint.route("/sku/<int:id>")
def get_sku(id):
sku = get(id)
return sku.to_json() if sku else ("Not found", 404)
# Get all SKUs
#routes_blueprint.route("/sku")
def get_all_sku():
return json.dumps([sku.to_json() for sku in get_all()])
#routes_blueprint.route("/sku/update", methods=["GET"])
def update_from_dataset():
return update_dataset()
# Get the 5 best prices for a SKU
#routes_blueprint.route("/sku/best", methods=["GET"])
def get_best_sku():
return json.dumps([sku.to_json() for sku in get_5_highest()])
# Update a SKU from an ID by increasing it's price by 21%
#routes_blueprint.route("/sku/<int:id>", methods=["PUT"])
def update_sku(id):
return update_21(id).to_json()
# Create SKU from form data
#routes_blueprint.route("/sku", methods=["POST"])
def create_sku():
sku = SKUModel(
sku=request.form["sku"],
product_title=request.form["product_title"],
quantity=request.form["quantity"],
price=request.form["price"],
)
return create(sku).to_json()
# Delete SKU from ID
#routes_blueprint.route("/sku/<int:id>", methods=["DELETE"])
def delete_sku(id):
return delete(id)
# Return the lowest price for a SKU
#routes_blueprint.route("/sku/lowest")
def get_lowest_sku():
return get_lowest().to_json()
# return median price of all SKU.
#routes_blueprint.route("/sku/median")
def get_median_sku():
return {"median": median([sku.price for sku in get_all()])}
core.py
from asyncio.log import logger
from app.models import SKUModel, db
from app.config import BasicConfig
import json
import logging
LOGGER = logging.getLogger(__name__)
def get(id):
try:
return db.session.query(SKUModel).get(id)
except Exception as e:
LOGGER.error("Error while getting SKU", e)
return None
return None
def get_all():
try:
return db.session.query(SKUModel).all()
except Exception as e:
LOGGER.error("Error while getting all SKU", e)
return None
I'm sure I missed something. I heard that db.create_all() should be use after importing my model and this is what I'm doing.
When you called db.create_all() inside app.py was imported from app.database. but the db inside of app.models is defined locally. Therefore there are no models in the db inside app.py and that is why it didn't create any tables. the simplest fix would be to update app.py and change:
from app.database import db
to
from app.models import db
running this extracted code works for me. If it doesn't for you then perhaps you have an issue writing to the /tmp/database.db file.
import flask_sqlalchemy
from flask import Flask
db = flask_sqlalchemy.SQLAlchemy()
class SKUModel(db.Model):
id = db.Column(db.Integer, primary_key=True)
sku = db.Column(db.String)
product_title = db.Column(db.String)
quantity = db.Column(db.Integer)
price = db.Column(db.Float)
SQLALCHEMY_PATH = "/tmp/database.db"
SQLALCHEMY_DATABASE_URI = "sqlite:///" + SQLALCHEMY_PATH
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = SQLALCHEMY_DATABASE_URI
db.init_app(app)
with app.app_context():
print(db)
db.create_all()
If this simple code works, then you have to look at your code to find out why it isn't running in the expected fashion.
from models import * #Import the SQLALCHEMY object from your
#models.py i.e. db = SQLAlchemy()
db.init_app(app)
#app.before_first_request
def create_database_tables():
with app.app_context():
db.create_all()
I have created a flask app. To store the data I have used sqlalchemy to create a db file.
My aim is to create an event calendar. I have decided to use FullCalendar.io . But the issue is that to feed the data it needs json file. Can anyone help me out
from flask import Flask, render_template, request, redirect
from flask.wrappers import Request
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
from sqlalchemy.orm import query
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///caldb.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
class CalData(db.Model):
date = db.Column(db.String(200), nullable=False)
title = db.Column(db.String(200), nullable=False, primary_key=True)
desc = db.Column(db.String(500), nullable=False)
time = db.Column(db.Integer, nullable=False)
link = db.Column(db.String(200), nullable=True)
def __repr__(self) -> str:
return f"{self.title} - {self.time}"
#app.route('/cal2')
def cal2():
cd = CalData.query.all()
return render_template("cal2.html", data=cd)
I'm not familiar with FullCalendar.io, but let's assume that you need a list of objects, each representing a single calendar event, in the format you've described in the ORM model. Then, you only need to put all the data from the instances you get from your query into dicts, and then send it out with jsonify:
from flask import jsonify
...
...
...
#app.route('/cal2')
def cal2():
result = []
calendar_entries = CalData.query.all()
for entry in calendar_entries:
obj = dict()
obj['date'] = entry.date
obj['title'] = entry.title
...
result.append(obj)
return jsonify(result)
I have created and configure a database and i am able to post data other than image or file and i am able to fetch the those data but i am unable to fetch the image data .
I am getting this error
> UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position
> 0: invalid start byte
This is my code :
from app import app, api, db, ma, Resource, request, images_schema, image_schema, ImageData, json, jsonify
import base64
from base64 import b64encode
class AllImage(Resource):
def get(self):
images = ImageData.query.all()
print(images)
return images_schema.dump(images)
def post(self):
if 'image' in request.files:
imagedata = request.files['image']
imageD = imagedata.filename
imageD = imagedata.read()
image = ImageData(
uname = request.form['uname'],
fname = request.form['fname'],
lname = request.form['lname'],
email = request.form['email'],
# image = base64.b64encode(imageD)
image= imageD
)
db.session.add(image)
db.session.commit()
# return image_schema.dump(image)
print(type(image))
# return "success"
return image_schema.dump(image)
api.add_resource(AllImage,'/image/data')
This is my DB config
from flask import Flask, json, jsonify, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow # new
from flask_restful import Api, Resource # new
from flask_cors import CORS
import datetime
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///image.db'
db = SQLAlchemy(app)
ma = Marshmallow(app)
api = Api(app)
CORS(app, resources={r"/loan/*": {"origins": "*"}})
class ImageData(db.Model):
id = db.Column(db.Integer, primary_key = True)
uname = db.Column(db.VARCHAR(20), nullable = True, unique=True)
fname = db.Column(db.VARCHAR(20), nullable = True)
lname = db.Column(db.VARCHAR(20), nullable = True)
email = db.Column(db.VARCHAR(50), nullable = True, unique=True)
# image = db.Column(db.LargeBinary, nullable=True)
image =db.Column(db.BLOB)
class ImageDataSchema(ma.Schema):
class Meta:
fields = ('id','uname','fname','lname','email','image')
image_schema = ImageDataSchema()
images_schema = ImageDataSchema(many=True)
I tried largebinary and blob type, what am i doing wrong here ?
A bit of an late answer -
you need to do something along the lines of
imageD = base64.b64encode(files.read())
but it's very likely your schema is then going to have trouble auto-dumping/serializing that when you try to "display" it.
I am using marshmallow and Flask Restplus for my one of API code where i have table with few columns i wan to post data into table using the POST method. In table i have one of column as BLOB which accept files JPEG or PDF and stores it into database.
I was able to save record into DB using the regular flask forms and now i want to enable to this functionality using the API.
For that I have bellow code where
Expense Model.py
from flask_sqlalchemy import SQLAlchemy
from app import db,ma
from datetime import datetime
from flask import url_for, current_app
from app.utils.exceptions import ValidationError
from sqlalchemy.dialects.mysql import LONGBLOB
from marshmallow import fields, pre_load, validate
from app.models.Person_model import PersonSchema
from app.models.ExpType_model import ExpTypeSchema
#This is model defination for the Expnese Table and its api calls with get post put all covered in here.
class Expenses(db.Model):
__tablename__ = 'Expesnes'
__table_args__ = {'extend_existing': True}
id = db.Column(db.Integer, primary_key=True)
per_id = db.Column(db.Integer, db.ForeignKey('Persons.id'), nullable=False)
U_id = db.Column(db.Integer, db.ForeignKey('Users.id'), nullable=False, index=True)
Exp_per_name = db.Column(db.String(64), index=True)
Exp_type_name = db.Column(db.String(100),index=True)
Exp_amt = db.Column(db.Float)
Exp_img = db.Column(db.LargeBinary)
Exp_FileName = db.Column(db.String(300))
Exp_date = db.Column(db.DateTime,index=True)
Exp_comm = db.Column(db.String(200))
def __init__(self,Exp_per_name,Exp_type_name,Exp_amt,Exp_img,Exp_FileName,Exp_date,Exp_comm):
self.Exp_per_name = Exp_per_name,
self.Exp_type_name = Exp_type_name,
self.Exp_amt = Exp_amt,
self.Exp_img = Exp_img,
self.Exp_FileName = Exp_FileName,
self.Exp_date = Exp_date,
self.Exp_comm = Exp_comm
# Custom validator
def must_not_be_blank(data):
if not data:
raise ValidationError('Data not provided.')
class ExpensesSchema(ma.Schema):
class Meta:
model = Expenses
id = fields.Integer(dump_only=True)
u_id = fields.Integer(dump_only=True)
Exp_per_name = fields.Nested(PersonSchema, validate=must_not_be_blank)
ExpType_name = fields.Nested(ExpTypeSchema,validate=must_not_be_blank)
Exp_amt =fields.Float(required=True)
Exp_img = fields.Raw()
Exp_FileName = fields.Str(allow_none=None)
Exp_comm = fields.Str(allow_none=None)
Expense_Resource.py File
from flask_restplus import Resource
import json
from flask import request
from flask_login import current_user, login_required
from app.models.Expense_model import Expenses, ExpensesSchema
from datetime import datetime
from app import db
expenses_schema = ExpensesSchema(many=True)
expense_schema = ExpensesSchema()
class ExpenseRes(Resource):
# This is method for all ExpTypes
#classmethod
#login_required
def get(cls):
exptypes = Expenses.query.filter_by(u_id=current_user.id)
invtypes = expenses_schema.dump(exptypes).data
return {'status': 'success', 'data': exptypes}, 200
# This is new records creation Persons
#classmethod
#login_required
def post(cls):
json_data = request.get_json(force=True)
if not json_data:
return {'message': 'No input data provided'}, 400
# Validate and deserialize input
data, errors = expense_schema.load(json_data)
print data
if errors:
return errors, 422
expensetype = Expenses.query.filter_by(u_id=current_user.id,
Exp_per_name=data['Exp_per_name'],
Exp_type_name = data['Exp_type_name'],
Exp_amt=data['Exp_amt'],
Exp_date=data['Exp_date']
).first()
if expensetype:
return {'message': 'Expense name already exists'}, 400
newexpensetype = Expenses(u_id=current_user.id,
Exp_per_name=data['Exp_per_name'],
Exp_type_name=data['Exp_type_name'],
Exp_img = request.json['binary'],
Exp_FileName= request.json['file_name'],
Exp_amt=data['Exp_amt'],
Exp_date=data['Exp_date'],
Exp_comm= data['Exp_comm']
)
try:
db.session.add(newexpensetype)
db.session.commit()
result = expense_schema.dump(newexpensetype).data
return {"status": 'success', 'data': result}, 201
except:
return {'message': 'An error occurred while creating Investment Type'}, 500
How to save file in to database and how do i pass file pay load to my API.
Is there way to only allow JPEG and PDF file with size less 15 MB only to go
Thank you for your time and appreciate your feedback.
I'm trying to get a todo_ID using GET method. I'm still new at using sqlalchemy and most of the things i have tried are telling me that sqlalchemy doesn't use them. Also, can someone tell me how to use HEAD, i want my methods to return http statuses, i kinda tried using them and imported the render template but when i try to use them it says it has no idea what they are.
this is my attempt at looking at a tutorial and making changes
from flask import Flask, jsonify,json, request, render_template, abort
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config.from_pyfile('Config.py')
db = SQLAlchemy(app)
class JsonModel(object): # Class for making objects JSON serializable
def as_dict(self):
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
class User(db.Model, JsonModel): # Class which is a model for the User table in the database
User_ID = db.Column(db.Integer, primary_key = True)
FirstName = db.Column(db.String(20))
LastName = db.Column(db.String(20))
def __init__(self,User_ID,FirstName, LastName):
self.User_ID = User_ID
self.FirstName = FirstName
self.LastName = LastName
class Todo(db.Model, JsonModel): # Class which is a model for the Todo table in the database
todo_ID = db.Column(db.Integer, primary_key = True)
UserID = db.Column(db.Integer, db.ForeignKey("user.User_ID"))
details = db.Column(db.String(30))
def __init__(self, UserID, details):
self.UserID = UserID
self.details = details
#app.route('/todo', methods = ['GET']) # Uses GET method to return all information in the database.
def index():
return json.dumps([u.as_dict() for u in User.query.all()+Todo.query.all()]), 201
#app.route('/todo/<int:todo_ID>', methods = ['GET'])
def get(todo_ID):
query = Todo.query.get()
return {'todo': [dict(zip(tuple(query.keys()), i)) for i in query.cursor if i[1] == todo_ID]}
#app.before_first_request #Creates everything before the first request.
def startup():
db.create_all()
if __name__ == '__main__':
app.run()
My most recent attempt was:
#app.route('/todo/<int:todo_ID>', methods = ['GET'])
def get(todo_ID):
query = Todo.query("select * from Todo")
return {'todo': [dict(zip(tuple(query.keys()), i)) for i in query.cursor if i[1] == todo_ID]}
And the error that I get is this.
query = Todo.query("select * from Todo")
TypeError: 'BaseQuery' object is not callable
127.0.0.1 - - [30/Nov/2016 21:15:28] "GET /todo/1 HTTP/1.1" 500 -
If you want to query Todo by primary key and only return one record you can use:
from flask import jsonify
#app.route('/todo/<int:todo_ID>', methods = ['GET'])
def get(todo_ID):
response = {}
todo = Todo.query.get(todo_ID)
response['id'] = todo.id
response['user_id'] = todo.UserID
response['details'] = todo.details
response['status_code'] = 201
return jsonify(response)
Or you can use Marshmallow to have a serializer for each of your models so it can serialize them for you automatically.
Not sure I understand your problem, if your intention is to return json output as response and you want to control the status code, you could
use jsonify
from flask import jsonify
#app.route('/todo/<int:todo_ID>', methods = ['GET'])
def get(todo_ID):
query = Todo.query("select * from Todo")
response = {'todo': [dict(zip(tuple(query.keys()), i)) for i in query.cursor if i[1] == todo_ID]}
response = jsonify(response)
response.status_code = 201
return response