<Object id> is not JSON serializable - python

i'm creating an API in python + Flask + marshmallow.
Here's the class + the first function (i don't already use the others )
import datetime
from marshmallow import Schema, fields, ValidationError, pre_load
from flask import Flask, jsonify
from flask_marshmallow import Marshmallow
from flask_sqlalchemy import SQLAlchemy
from flask import request
import os
app = Flask(__name__)
basedir = os.path.abspath(os.path.dirname(__file__))
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'User.sqlite')
# Order matters: Initialize SQLAlchemy before Marshmallow
db = SQLAlchemy(app)
ma = Marshmallow(app)
class User(db.Model):
userid = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30))
badgenum = db.Column(db.String(30))
mail = db.Column(db.String(30))
status = db.Column(db.String(30))
admin = db.Column(db.String(30))
def __init__(self, userid, name, badgenum, mail, status, admin):
self.userid = userid
self.name = name
self.badgenum = badgenum
self.mail = mail
self.status = status
self.admin = admin
class UserSchema(ma.Schema):
class Meta:
fields = ('userid', 'name', 'badgenum', 'mail', 'status', 'admin')
user_schema = UserSchema()
users_schema = UserSchema(many=True)
#app.route("/User", methods=["POST"])
def add_User():
userid = request.json['userid']
name = request.json['name']
badgenum = request.json['badgenum']
mail = request.json['mail']
status = request.json['status']
admin = request.json['admin']
new_user = User(userid, name, badgenum, mail, status, admin)
db.session.add(new_user)
db.session.commit()
return jsonify(new_user)
I tested the function add_User using Postman with this json request :
{
"userid" : 1,
"name" : "name1",
"badgenum" : "66897",
"mail" : "ghh#orange.fr",
"status" : "on",
"admin" : "f"
}
and i got this error:
TypeError: <User 145> is not JSON serializable

User has its own autogenerated id user.id which is ObjectId.
You need custom json encoder to encode ObjectId field.
import json, bson
def json_response(obj, cls=None):
response = make_response(json.dumps(obj, cls=cls))
response.content_type = 'application/json'
return response
class MongoJsonEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, bson.ObjectId):
return str(obj)
return json.JSONEncoder.default(self, obj)
Now try to call return json_response(new_user, cls=MongoJsonEncoder)

Related

SQLAlchemy Marshmallow return empy JSON flask

I'm trying to get a JSON with all registered users, but the request returns an empty object.
Model and Schemas Model
from dataclasses import field
from utils.database import db, ma
from flask_bcrypt import check_password_hash
class Gym(db.Model):
id = db.Column(db.Integer, primary_key=True)
gym_nombre = db.Column(db.String(100))
password = db.Column(db.String(200))
direccion = db.Column(db.String(200))
propietario = db.Column(db.String(200))
telefono = db.Column(db.String(200))
def __init__(self, gym_nombre, password,direccion, propietario,telefono):
self.gym_nombre = gym_nombre
self.password = password
self.direccion = direccion
self.propietario = propietario
self.telefono = telefono
class GymSchemas(ma.Schema):
class meta:
model = Gym
gyms_schemas = GymSchemas(many=True)
gym_schemas = GymSchemas()
Route "usuarios"
from flask import Blueprint, jsonify
from models.model_usuarios import Gym, gyms_schemas
usuarios = Blueprint("usuarios", __name__)
#usuarios.route("/usuarios", methods=['GET'])
def getUsers():
all_users = Gym.query.all()
return jsonify(gyms_schemas.dump(all_users))
Request in postman

Flask-Marshmallow returning empty json and saying my schema is invalid

I am trying to use Marshmallow to serialize my Message Model class yet it is returning an empty json object whenever I try to get the data and after I debugged the program with
print(messagesSchema.validate(messages))
It said: 0: {'_schema': ['Invalid input type.']}
This is my Chat model, Message Model and my MessageSchema model
class Chat(db.Model):
id = db.Column(db.Integer, primary_key=True)
user1 = db.Column(db.String(150), unique=True)
user2 = db.Column(db.String(150), unique=True)
room = db.Column(db.String(300), unique=True)
messages = db.relationship("Message", backref="chat")
class Message(db.Model):
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.String(300))
username = db.Column(db.String(150))
chat_id = db.Column(db.Integer, db.ForeignKey("chat.id"))
class MessageSchema(ma.SQLAlchemySchema):
class Meta:
model = Message
This is my socketHandler file
messages = Chat.query.filter_by(room=room).first().messages
messagesSchema = MessageSchema(many=True)
print(messagesSchema.validate(messages))
output = messagesSchema.dump(messages)
print(output)
print(jsonify(output))
and the init.py file if anybody is wondering
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from os import path
from flask_login import LoginManager
db = SQLAlchemy()
DB_NAME = "database.db"
ma = Marshmallow()
def create_app():
app = Flask(__name__)
app.config["SECRET_KEY"] = "secret"
app.config["SQLALCHEMY_DATABASE_URI"] = f'sqlite:///{DB_NAME}'
db.init_app(app)
from .views import views
from .auth import auth
from .slashUrl import slashUrl
app.register_blueprint(slashUrl, url_prefix="/")
app.register_blueprint(views, url_prefix="/home/")
app.register_blueprint(auth, url_prefix="/auth/")
from .models import User
create_database(app)
login_manager = LoginManager()
login_manager.login_view = "auth.login"
login_manager.login_message_category = "dangerAlert"
login_manager.init_app(app)
#login_manager.user_loader
def load_user(id):
return User.query.get(int(id))
return app
def create_database(app):
if not path.exists("website/" + DB_NAME):
db.create_all(app=app)
print("Database created")
From my print statements I get
{0: {'_schema': ['Invalid input type.']}}
[{}]
<Response 9 bytes [200 OK]>
If anybody knows how to fix this your help would be greatly appreciated!

AttributeError: 'NoneType' object has no attribute 'read' when posting image to database

I am trying to troubleshoot my python database by posting an image to it. However whenever I send the post request I receive the following error.
AttributeError: 'NoneType' object has no attribute 'read'
line 98, in add_file
new_file = File(name, file_type, data.read())
AttributeError: 'NoneType' object has no attribute 'read'
Here's the code I have as of now.
from flask import Flask, request, jsonify, send_file
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from flask_cors import CORS
from flask_heroku import Heroku
import io
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "postgres://wbzxqshctunral:df393ee4891d46d62fdfa0daf99f0cd5ba4563268340386d5caf85904f470c71#ec2-34-224-229-81.compute-1.amazonaws.com:5432/drj57s4cb9qkq"
db = SQLAlchemy(app)
ma = Marshmallow(app)
heroku = Heroku(app)
CORS(app)
class VehicleInformation(db.Model):
id = db.Column(db.Integer, primary_key=True)
make = db.Column(db.String(), nullable=False)
model = db.Column(db.String(), nullable=False)
mpgE = db.Column(db.Integer, nullable=False)
year = db.Column(db.Integer, nullable=False)
description = db.Column(db.String(), nullable=False)
def __init__(self, make, model, mpgE, year, description):
self.make = make
self.model = model
self.mpgE = mpgE
self.year = year
self.description = description
class VehicleInformationSchema(ma.Schema):
class Meta:
fields = ("id", "make", "model", "mpgE", "year", "description")
vehicle_information_schema = VehicleInformationSchema()
multiple_vehicle_information_schema = VehicleInformationSchema(many=True)
class File(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(), nullable=False)
file_type = db.Column(db.String(), nullable=False)
data = db.Column(db.LargeBinary, nullable=False)
def __init__(self, name, file_type, data):
self.name = name
self.file_type = file_type
self.data = data
class FileSchema(ma.Schema):
class Meta:
fields = ("id", "name", "file_type")
file_schema = FileSchema()
files_schema = FileSchema(many=True)
#app.route("/vehicle/add", methods=["POST"])
def add_vehicle():
if request.content_type != "application/json":
return jsonify("Error: Data must be sent as JSON")
vehicle_data = request.get_json()
make = vehicle_data.get("make")
model = vehicle_data.get("model")
mpgE = vehicle_data.get("mpgE")
year = vehicle_data.get("year")
description = vehicle_data.get("description")
record = VehicleInformation(make, model, mpgE, year, description)
db.session.add(record)
db.session.commit()
return jsonify("Vehicle has been added successfully")
#app.route("/vehicle/all_vehicles", methods=["GET"])
def get_all_vehicles():
all_vehicles = db.session.query(VehicleInformation).all()
return jsonify(multiple_vehicle_information_schema.dump(all_vehicles))
#app.route("/vehicle/delete", methods=["DELETE"])
def delete_vehicle(id):
vehicle_data = db.session.query(VehicleInformation).filter(VehicleInformation.id == id).first()
db.session.delete(vehicle_data)
db.session.commit()
return jsonify("Vehicle has been eradicated")
#app.route("/file/add", methods=["POST"])
def add_file():
name = request.form.get("name")
file_type = request.form.get("type")
data = request.files.get("data")
new_file = File(name, file_type, data.read())
db.session.add(new_file)
db.session.commit()
return jsonify("File added successfully")
#app.route("/file/get/data", methods=["GET"])
def get_file_data():
file_data = db.session.query(File).all()
return jsonify(files_schema.dump(file_data))
#app.route("/file/get/<id>", methods=["GET"])
def get_file(id):
file_data = db.session.query(File).filter(File.id == id).first()
return send_file(io.BytesIO(file_data.data),
attachment_filename=file_data.name,
mimetype=file_data.file_type)
#app.route("/file/delete/<id>", methods=["DELETE"])
def delete_file(id):
file_data = db.session.query(File).filter(File.id == id).first()
db.session.delete(file_data)
db.session.commit()
return jsonify("File Deleted Successfully")
if __name__ == "__main__":
app.run(debug=True)
I appreciate the help in advance!
It seems like you aren't handling error cases where, for example, data isn't passed to the request:
#app.route("/file/add", methods=["POST"])
def add_file():
name = request.form.get("name")
file_type = request.form.get("type")
data = request.files.get("data")
if data is None:
return jsonify("Missing file!")
# You should probably have the same checkers for name and file_type
new_file = File(name, file_type, data.read())
db.session.add(new_file)
db.session.commit()
return jsonify("File added successfully")

HTTP/1.0 500 INTERNAL SERVER ERROR in flask

I have written a CRUD API in flask but whenever I try to make a GET request, I get this error saying "internal server error". POST requests are also not working for me. POST return a 400 error saying that it is a bad request. What am I doing wrong here?
Following is my run.py file
from flask import Flask, jsonify, abort, request
from flask_restless import APIManager
from flask_sqlalchemy import SQLAlchemy
from flask import make_response
app = Flask(__name__)
app.config.from_pyfile('config.py')
db = SQLAlchemy(app)
class User(db.Model):
username = db.Column(db.String(50), primary_key=True)
password = db.Column(db.String(50))
name = db.Column(db.String(20))
def __init__(self, name, uname, pword):
self.username = uname
self.name = name
self.password = pword
#app.route('/api/users/', methods = ['GET'])
def index():
return jsonify({'users': User.query.all()})
#app.route('/api/<string:username>/')
def get_user(username):
return jsonify({'users': User.query.get(username)})
#app.errorhandler(404)
def not_found(error):
return make_response(jsonify({'error': 'Not found'}), 404)
#app.route('/api/users/', methods = ['POST'])
def create_user():
if not request.json or not 'name' in request.json:
abort(400)
user = User(request.json.username, request.json.get('name', ''), request.json.get('password',''))
db.session.add(user)
db.session.commit()
return jsonify( { 'User': user } ), 201
#app.route('/api/users/<string:username>', methods = ['DELETE'])
def delete_user(username):
db.session.delete(Users.query.get(username))
db.session.commit()
return jsonify( { 'result': True } )
#app.route('/api/users/<string:username>', methods = ['PUT'])
def update_user(username):
user = User.query.get(username)
user.username = request.json.get('username', ser.username)
user.name = request.json.get('name',user.name)
user.focus = request.json.get('password', user.password)
db.session.commit()
return jsonify( { 'user': user } )
if __name__ == '__main__':
app.run()
Following is my config.py file
import os
basedir = os.path.abspath(os.path.dirname(__file__))
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'app.sqlite')
SQLALCHEMY_MIGRATE_REPO = os.path.join(basedir, 'db_repository')
What am I doing wrong here? Any help would be highly appreciated.
You need to 'create' the User table after you define its class, otherwise it doesn't show up in memory. http://flask-sqlalchemy.pocoo.org/2.1/api/#flask.ext.sqlalchemy.SQLAlchemy.create_all
The way to do it is:
class User(db.Model):
username = db.Column(db.String(50), primary_key=True)
password = db.Column(db.String(50))
name = db.Column(db.String(20))
def __init__(self, name, uname, pword):
self.username = uname
self.name = name
self.password = pword
db.create_all()
...the rest of your code

Eve Authentication with bcrypt driver.db error when sending curl request

I use bcrypt for the authentication of my resources and have accounts stored in mydatabase with username and passwords. I have stored the passwords manually as hashes in the database as follows:
I started the python bash and typed in following code:
import bcrypt
password = u'passwordtobehashed'
password_hashed = bcrypt.hashpw(password, bcrypt.gensalt())
print (password_hashed)
Then i copied the output of print and stored it in the account table via a POST Request(w/o authentication):
curl -d '{"username": "someuser", "password": "somehashedpassword", "roles_id": 1}' -H 'Content-Type: application/json' http://127.0.0.1:5000/account
Well I use SQLAlchemy and eve is also up to date (Version: 0.7.1).
Well I request for example the people resource using Bcrypted Authentication as follows:
curl -u username 127.0.0.1:5000/people
Then I enter my password and I get following error:
File "/home/vagrant/erpghost/restapi/oldtrivial.py", line 57, in check_auth
accounts = app.data.driver.db['account']
AttributeError: 'SQLAlchemy' object has no attribute 'db'
For some reason the db attribute is not available. I also tried to use Eve.app.data.driver.db and I tried importing current_app from flask but that all did not work.
Well here is my code:
oldtrivial.py
from eve.auth import BasicAuth
from eve import Eve
from eve_sqlalchemy import SQL
from eve_sqlalchemy.validation import ValidatorSQL
import bcrypt
from connection import connect
from connection import Base
con, meta = connect()
Base.metadata.create_all(con)
class BCryptAuth(BasicAuth):
def check_auth(self, username, password, allowed_roles, resource, method):
accounts = app.data.driver.db['account']
account = accounts.find_one({'username': username})
return account and \
bcrypt.hashpw(password, account['password']) == account['password']
app = Eve(validator=ValidatorSQL, data=SQL, auth=BCryptAuth)
db = app.data.driver
Base.metadata.bind = db.engine
db.Model = Base
db.create_all()
if __name__ == '__main__':
app.run(debug=True, use_reloader=False)
tables.py
from sqlalchemy.orm import column_property
from sqlalchemy import Column, Integer, String, DateTime, func, ForeignKey
from connection import connect
from eve.auth import BasicAuth
from connection import Base
from sqlalchemy.orm import relationship
con, meta = connect()
class CommonColumns(Base):
__abstract__ = True
_created = Column(DateTime, default=func.now())
_updated = Column(DateTime, default=func.now(), onupdate=func.now())
_etag = Column(String(40))
class People(CommonColumns):
__tablename__ = 'people'
_id = Column(Integer, primary_key=True, autoincrement=True)
firstname = Column(String(80))
lastname = Column(String(120))
fullname = column_property(firstname + " " + lastname)
class Roles(CommonColumns):
__tablename__ = 'roles'
_id = Column(Integer, primary_key=True, autoincrement=True)
role = Column(String(80))
class Account(CommonColumns):
__tablename__ = 'account'
_id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(50), nullable=False, unique=True)
password = Column(String(200), nullable=False)
roles = relationship("Roles", backref="account")
roles_id = Column(Integer, ForeignKey('roles._id'))
settings.py
from eve_sqlalchemy.decorators import registerSchema
from eve.utils import config
from tables import People
from tables import Account
from tables import Roles
registerSchema('people')(People)
registerSchema('roles')(Roles)
registerSchema('account')(Account)
DOMAIN = {
'people': People._eve_schema['people'],
'roles': Roles._eve_schema['roles'],
'account': Account._eve_schema['account'],
}
DOMAIN['account'].update({
'additional_lookup': {
'url': 'regex("[\w]+")',
'field': 'username'
},
'cache_control': '',
'cache_expires': 0,
'allowed_roles': ['superuser', 'admin'],
'authentication': None,
})
SQLALCHEMY_DATABASE_URI = 'postgresql://databaseuser:password#localhost:5432/database'
RESOURCE_METHODS = ['GET', 'POST']
ITEM_METHODS = ['GET', 'DELETE', 'PATCH', 'PUT']
DEBUG = True
config.ID_FIELD = config.ITEM_LOOKUP_FIELD = '_id'
DOMAIN['people']['id_field'] = config.ID_FIELD
DOMAIN['roles']['id_field'] = config.ID_FIELD
DOMAIN['account']['id_field'] = config.ID_FIELD
Hope someone can help me out.
Something along these lines should work:
from flask import current_app
from tables import Account
# ...
def check_auth(...):
session = current_app.data.driver.session
return session.query(Account) \
.filter(Account.username == username,
Account.password == hashed_password) \
.count() > 0
I guess you've tried to mimic the code in http://python-eve.org/authentication.html#basic-authentication-with-bcrypt ? This is for using Mongo-DB instead of SQLAlchemy.

Categories

Resources