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
Related
Longtime lurker, first time poster.
Running SQLAlchemy via Flask and Docker.
This test keeps failing:
class TestLogin(ViewTestMixin):
def test_login_disable(self):
""" Fail to login due to disabled account. """
response = self.login(identity='disabled#local.host')
assert_status_with_message(200, response,
> 'This account has been disabled.')
With a traceback of:
self = <flowstate.tests.user.test_views.TestLogin object at 0x7fbed057dd90>
def test_login_disable(self):
""" Fail to login due to disabled account. """
response = self.login(identity='disabled#local.host')
assert_status_with_message(200, response,
> 'This account has been disabled.')
flowstate/tests/user/test_views.py:23:
status_code = 200, response = <Response 3007 bytes [200 OK]>, message = 'This account has been disabled.'
def assert_status_with_message(status_code=200, response=None, message=None):
""" Check to see if a message is contained within a response. """
assert response.status_code == status_code
> assert message in str(response.data)
E AssertionError
Here are the components to the previous test:
self.login (via ViewTestMixin)
class ViewTestMixin(object):
""" Automatically load in a session and client. """
#pytest.fixture(autouse=True)
def set_common_fixtures(self, session, client):
self.session = session
self.client = client
def login(self, identity='admin#local.host', password='password'):
""" Login a specific user. """
return login(self.client, identity, password)
def logout(self):
""" Logout a specific user. """
return logout(self.client)
def login(client, username='', password=''):
""" Login a specific user. """
user = dict(identity=username, password=password)
response = client.post(url_for('user.login'), data=user,
follow_redirects=True)
return response
def logout(client):
""" Logout a specific user. """
return client.get(url_for('user.logout'), follow_redirects=True)
assert_status_with_message
def assert_status_with_message(status_code=200, response=None, message=None):
""" Check to see if a message is contained within a response. """
assert response.status_code == status_code
assert message in str(response.data)
Here is the specific view function it is testing:
#user.route('/login', methods=['GET', 'POST'])
#anonymous_required()
def login():
form = LoginForm(next=request.args.get('next'))
if form.validate_on_submit():
u = User.find_by_identity(request.form.get('identity'))
if u and u.authenticated(password=request.form.get('password')):
if login_user(u, remember=True) and u.is_active():
u.update_activity_tracking(request.remote_addr)
next_url = request.form.get('next')
if next_url:
return redirect(safe_next_url(next_url))
return redirect(url_for('user.settings'))
else:
flash('This account has been disabled.', 'error')
else:
flash('Identity or password is incorrect.', 'error')
return render_template('user/login.html', form=form)
Substituting "This account has been disabled." with "Identity or password is incorrect." in test_login_disable causes it to pass.
So the problem must be with:
if u and u.authenticated(password=request.form.get('password')):
Updating that line to only:
if u.authenticated(password=request.form.get('password')):
gives a new traceback of:
def test_login_disable(self):
""" Fail to login due to disabled account. """
response = self.login(identity='disabled#local.host')
flowstate/tests/user/test_views.py:20:
#user.route('/login', methods=['GET', 'POST'])
#anonymous_required()
def login():
form = LoginForm(next=request.args.get('next'))
if form.validate_on_submit():
u = User.find_by_identity(request.form.get('identity'))
if u.authenticated(password=request.form.get('password')):
E AttributeError: 'NoneType' object has no attribute 'authenticated'
flowstate/blueprints/user/views.py:39: AttributeError
So my database isn't updating this specific user (disabled#local.host).
Relevant code:
forms.py
class LoginForm(Form):
next = HiddenField()
identity = StringField('Username or email',
[DataRequired(), Length(3, 254)])
password = PasswordField('Password', [DataRequired(), Length(8, 128)])
models.py
class User(UserMixin, ResourceMixin, db.Model):
ROLE = OrderedDict([
('member', 'Member'),
('admin', 'Admin')
])
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
# Authentication
role = db.Column(db.Enum(*ROLE, name='role_types', native_enum=False),
index=True, nullable=False, server_default='member')
active = db.Column('is_active', db.Boolean(), nullable=False,
server_default='1')
username = db.Column(db.String(24), unique=True, index=True)
email = db.Column(db.String(255), unique=True, index=True, nullable=False,
server_default='')
password = db.Column(db.String(128), nullable=False, server_default='')
# Activity tracking
sign_in_count = db.Column(db.Integer, nullable=False, default=0)
current_sign_in_ip = db.Column(db.String(45))
last_sign_in_ip = db.Column(db.String(45))
conftest.py
#pytest.yield_fixture(scope='session')
def app():
""" Setup the Flask app and app context once for the testing session. """
db_uri = '{0}_test'.format(settings.SQLALCHEMY_DATABASE_URI)
params = {
'DEBUG': False,
'TESTING': True,
'WTF_CSRF_ENABLED': False,
'SQLALCHEMY_DATABASE_URI': db_uri
}
_app = create_app(settings_override=params)
ctx = _app.app_context()
ctx.push()
yield _app
ctx.pop()
#pytest.fixture(scope='function')
def users(db):
""" Set up user fixtures once per test. """
db.session.query(User).delete()
users = [
{
'role': 'admin',
'email': 'admin#local.host',
'password': 'password'
},
{
'active': False,
'email': 'disabled#local.host',
'password': 'password'
}
]
for user in users:
db.session.add(User(**user))
db.session.commit()
return db
settings.py
db_uri = 'postgresql://flowstate:devpassword#postgres:5432/flowstate'
SQLALCHEMY_DATABASE_URI = db_uri
SQLALCHEMY_TRACK_MODIFICATIONS = False
this is my app.py code but i am receiving unbound local error after adding another path for delete and creating the function delete.
from crypt import methods
from email.policy import default
from flask import Flask, render_template, request, redirect
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///todo.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
class Todo(db.Model):
sno = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(200), nullable=False)
desc = db.Column(db.String(200), nullable=False)
date_created = db.Column(db.DateTime, default=datetime.utcnow)
def __repr__(self) -> str:
return f"{self.sno} - {self.title}"
#app.route('/', methods=['GET', 'POST'])
def hello_world():
if request.method == 'POST':
title = request.form["title"]
desc = request.form["desc"]
todo = Todo(title=title, desc=desc)
db.session.add(todo)
db.session.commit()
mytodo = Todo.query.all()
return render_template('index.html', mytodo=mytodo)
#app.route('/delete/<int:sno>')
def delete(sno):
todo = Todo.query.filter_by(sno==sno).first()
db.session.delete(todo)
db.session.commit()
return redirect('/')
if __name__ == "__main__":
app.run(debug=True, port=8000)
Below i've added the screenshot of the errors i am getting even when i just visit the root url.
Problem
If the server receive a GET /, then you won't define te title variable, but would try to use it at Todo(title=title, desc=desc) so an error
Fix
All the creation code should be in the if POST branch
#app.route('/', methods=['GET', 'POST'])
def hello_world():
if request.method == 'POST':
title = request.form["title"]
desc = request.form["desc"]
todo = Todo(title=title, desc=desc)
db.session.add(todo)
db.session.commit()
mytodo = Todo.query.all()
return render_template('index.html', mytodo=mytodo)
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)
I keep getting an error on the url when I try to implement POST in my API. I keep getting the error in the URL saying METHOD not Allowed for this URL. Am I missing something? Does POST not work directly when u try to open the server?? I'm soooo lost.
from flask import Flask, jsonify,json, request, abort
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config.from_pyfile('Config.py')
db = SQLAlchemy(app)
db.create_all()
class JsonModel(object):
def as_dict(self):
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
class User(db.Model, JsonModel):
User_ID = db.Column(db.Integer, primary_key = True)
FirstName = db.Column(db.String(20))
LastName = db.Column(db.String(20))
def __init__(self,FirstName, LastName):
self.FirstName = FirstName
self.LastName = LastName
class Todo(db.Model, JsonModel):
todo_ID = db.Column(db.Integer, primary_key = True)
UserID = db.Column(db.Integer, db.ForeignKey('User_ID'))
details = db.Column(db.String(30))
def __init__(self,details):
self.details = details
#app.route('/', methods = ['GET'])
def index():
return json.dumps([u.as_dict() for u in User.query.all()+Todo.query.all()])
#app.route('/todo/<int:UserID>', methods = ['GET'])
def get(UserID):
return (list[Todo.query.get(UserID)])
#app.route('/p/', methods = ['POST'])
def create_dev():
if not request.json or not 'name' in request.json:
abort(400)
dev = Todo(request.json.details,request.json.get('details',''))
db.session.add(dev)
db.session.commit()
return json.dumps([{'dev': dev}]), 201
if __name__ == '__main__':
app.run()
You should add GET method to list of allowed methods. When You try to load page, You first need to get page itself using GET method. Then, after filling something on the page, You use POST method to pass some data to the app. In the app, You should check with which method function is called. Something like this:
#app.route('/p', methods=['GET', 'POST'])
def create_dev():
if request.method == 'GET':
return render_template('p_page.html')
# If You get to this line, it means it is POST method
do_something_here()
I want to connect sql using sqlalchemy on flask
but it can't
there is a error
sqlalchemy.exc.OperationalError: (_mysql_exceptions.OperationalError) (1049, "Unknown database 'alchemy_new'")
I can't solve this problem.
# -*- coding:utf-8 -*-
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_restful import reqparse, abort, Api, Resource
app = Flask(__name__)
api = Api(app)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:12345#localhost/catchat'
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)
password = db.Column(db.String(120), unique=True)
def __init__(self, username, email, password):
self.username = username
self.email = email
self.password = password
def __repr__(self):
return '<User %r>' % self.username
user_inf = {'user1' : {'username':'jaeyeonkim','email':'jaeyeon93#naver.com', 'password':'12345'}}
def not_exist(user_id):
if user_id not in user_inf:
abort(404, message="{} does not exist".format(user_id))
parser = reqparse.RequestParser()
parser.add_argument('username')
parser.add_argument('email')
parser.add_argument('password')
class user(Resource):
def get(self, user_id):
not_exist(user_id)
return user_inf[user_id]
def delete(self,user_id):
not_exist(user_id)
del user_inf[user_id]
return '', 200
def put(self,user_id):
args = parser.parse_args()
not_exist(user_id)
a = {}
a['username'] = args['username']
a['email'] = args['email']
a['password'] = args['password']
user_inf[user_id] = a
return a, 200
class userlist(Resource):
def get(self):
return user_inf
def post(self):
args = parser.parse_args()
new_user = user_inf(args['username'], args['email'], args['password'])
new_user_id = db.session.add(new_user)
db.session.commit()
return new_user_id, 200
api.add_resource(userlist, '/users')
api.add_resource(user,'/api/v1/users/<user_id>')
if __name__ == '__main__':
app.run(debug=True)
First of all I have no idea why password shall be unique ;)
Secondly it looks like sqlalchemy doesn't recognize you connection string.
Try:
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql:////root:12345#localhost/catchat'
According to the following codeļ¼
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:12345#localhost/catchat'
Your database is 'catchat'.Please create it.
mysql -u root -p12345
create database catchat;
And create table before use it.
db.create_all()
Finally, just a suggestion: don't use flask-restful to define api, please choose
#app.route("/user", methods=["GET"]), it is simple, easy and useful.
Trust me. Two weeks ago, I just rewrite my code from flask-restful to app.route.