I'm trying to setup a json service with a dynamic route: /action/{id}
I get a 404 when I navigate to: http://example.com:8080/action/test
Based on this documentation, it seems like my routing is configured correctly, but it is not.
Any idea on what I'm doing wrong here?
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.view import view_config
#view_config(route_name="action", renderer="json")
def run_action(self):
id = self.request.matchdict['id']
return {'id': id}
def main():
config = Configurator()
config.add_route('action', '/action/{id}')
app = config.make_wsgi_app()
return app
if __name__ == '__main__':
app = main()
server = make_server('0.0.0.0', 8080, app)
server.serve_forever()
Put a call to config.scan() in your main() function:
def main():
config = Configurator()
config.add_route('action', '/action/{id}')
config.scan()
app = config.make_wsgi_app()
return app
The #view_config decorator doesn't do anything on its own. You must call config.scan(), which will then go and look for all #view_config declarations that have a route_name matching one of config's routes:
config.add_route('foo')
config.scan()
will detect:
#view_config(route_name="foo")
Also, if you're going to have run_action as a standalone function (and not a class method), it should accept a single argument, 'request' (not 'self' as in your excerpt):
#view_config(route_name="action", renderer="json")
def run_action(request):
id = request.matchdict['id']
return {'id': id}
If you do plan on having run_action as a class method, you need to initialize that class correctly, and decorate just the class method:
class MyArbitraryClass():
def __init__(self, request):
self.request = request
#view_config(route_name="action", renderer="json")
def run_action(self):
id = request.matchdict['id']
return {'id': id}
Pyramid Documentation:
http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/project.html
views.py
from pyramid.view import view_config
#view_config(route_name="action", renderer="json")
def run_action(request):
id = request.matchdict['id']
return {'id': id}
def main():
config = Configurator()
config.add_route('action', '/action/{id}')
app = config.make_wsgi_app()
return app
_ init_.py
from pyramid.config import Configurator
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
config = Configurator(settings=settings)
config.add_route('action', '/action/{id}')
config.scan()
return config.make_wsgi_app()
Related
i am trying to generate Flask route using a basic DI i.e mapping methods as route handlers, i am a total beginner at Flask so mind my basic skills
class myClass():
def __init__(self):
self.dbConnObj = DbToolsMySql('someconnection', 'slave')
self.dbConnObj.connect()
self.blueprint = Blueprint('myClass', __name__)
self.blueprint.add_url_rule('/my_method', view_func=self.my_method)
def my_method(self, event):
retun "hello"
and then in my handler file
from flask import Flask
from flask_restful import Api, Resource
from src.app.services.myClassimport myClass
app = Flask(__name__)
app.register_blueprint(myClass.blueprint)
if __name__ == "main":
app.run()
Quite simple ehh???? but not working... i am getting following message
Not Found The requested URL was not found on the server. If you
entered the URL manually please check your spelling and try again.
typically you add routes to the Flask app with decorators like so:
app = Flask(__name__)
#app.route('/some-endpoint')
def some_endpoint_handler():
# do something
pass
Or you can add without a decorator like so:
def some_endpoint_handler():
# do something
pass
app = Flask(__name__)
app.route('/some-endpoint', methods=['GET'])(some_endpoint_handler)
So in your scenario, you can pass the app.route call to your myClass object and set the route like this:
class myClass():
def __init__(self, router):
self.dbConnObj = DbToolsMySql('someconnection', 'slave')
self.dbConnObj.connect()
self.blueprint = Blueprint('myClass', __name__)
#self.blueprint.add_url_rule('/my_method', view_func=self.my_method)
router('/my_method', ['GET'])(self.my_method)
def my_method(self, event):
retun "hello"
myObj = myClass( app.route )
or, invert the dependency:
app = Flask(__name__)
#app.route(myClass.blueprint.some_endpoint_string)
def some_endpoint_handler():
myClass.blueprint.call_some_endpoint_handler()
pass
if __name__ == "main":
app.run()
I am creating a simple REST server in pyramid by following the tutorial. When I write the class and server startup code in same file, it works as expected. But when I move the class file to separate file it is not working.
The following is my project structure.
The code I have written is
1. server.py
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from test_view_defaults import RESTView
if __name__ == '__main__':
with Configurator() as config:
config.add_route('rest', '/rest')
config.scan()
app = config.make_wsgi_app()
server = make_server('0.0.0.0', 6543, app)
server.serve_forever()
2. test_view_defaults.py
from pyramid.response import Response
from pyramid.view import view_config
from pyramid.view import view_defaults
#view_defaults(route_name='rest')
class RESTView(object):
def __init__(self, request):
self.request = request
#view_config(request_method='GET')
def get(self):
return Response('get')
#view_config(request_method='POST')
def post(self):
return Response('post')
#view_config(request_method='DELETE')
def delete(self):
return Response('delete')
When I request http://localhost:6543/rest it gives 404 error.
Could anyone help me to find what I am doing wrong ?
I solved the problem as below
Created a directory (module) named 'api'
Moved the class file test_view_defaults.py into the above created directory
Changed the scan method as config.scan(package='api')
The changed server.py is as follows
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
if __name__ == '__main__':
with Configurator() as config:
config.add_route('rest', '/rest')
config.scan(package='api')
app = config.make_wsgi_app()
server = make_server('0.0.0.0', 6543, app)
server.serve_forever()
I've a flask web app in which I authenticate to the "core" DB as admin.
MONGO_URI = "mongodb://myUserAdmin:abc123#localhost:27017/test?authSource=admin"
mongo = PyMongo(app)
# ... and I am able to interact with the DB
flash(mongo.db.user.find_one())
now, I want to create sub-DBs for each user of the app and let him modify only its specific DB (or table). How can I configure flask to manage that? I tried to look in web but found no solutions.
Thanks in advance for any help!
You can do something like this
Create Authentication middleware
class UserAuthenticationMiddleware:
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
'''
Authenticate the user here
'''
self.app.user = {'_id': ObjectId('ddddddddssssssssssss')} # authenticate the user and get it from db
return self.app(environ, start_response)
Then create a Database middleware to get a db for the user
class DbMiddleware(object):
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
if hasattr(self.app, 'user'):
# create a database by user id or you can use any unique field here
self.app.db = self.app.db_client[str(self.app.user._id)]
else:
self.app.db = self.app.db_client['default_db']
return self.app(environ, start_response)
Then in create app instance
from flask import Flask
if __name__ == '__main__':
app = Flask(__name__)
app.db_client = MongoClient()
app = DbMiddleware(UserAuthenticationMiddleware(app))
app.run()
I have a simple create_app function in app/__init__.py:
from flask import Flask
from flask_bootstrap import Bootstrap
from flask_login import LoginManager
from flask_pymongo import PyMongo
from .user_management import User
from config import app_config
login_manager = LoginManager()
mongo = PyMongo()
....
def create_app(config):
app = Flask(__name__, instance_relative_config=True, static_folder='static')
login_manager.init_app(app)
login_manager.login_message = 'You must be logged in to view this page'
login_manager.login_view = 'auth.login'
Bootstrap(app)
app.config.from_object(app_config[config])
app.config.from_pyfile('config.py')
if app.testing:
mongo.init_app(app, config_prefix='MONGO2')
else:
mongo.init_app(app)
....
return app
And my config:
class Config():
DEBUG = False
MONGO_HOST = 'localhost'
MONGO_PORT = 27017
....
class DevelopmentConfig(Config):
DEBUG = True
DEVELOPMENT = True
class TestingConfig(Config):
TESTING = True
DEBUG = False
CSRF_ENABLED = False
MONGO2_DBNAME = 'test'
....
app_config = {
'testing': TestingConfig,
'development': DevelopmentConfig,
'production': ProductionConfig
}
Throughout the app, I import the mongo instance from this file make use of it throughout the app. However, I can not find a find to set up a new mongo instance that uses the 'test' database without using the app context, as I'm doing here
My unit test file looks like this.
from app import create_app
import unittest
from app import mongo
class TestCase(unittest.TestCase):
def setUp(self):
app = create_app('testing')
self.app = app
def tearDown(self):
pass
def test_mongo(self):
with self.app.app_context():
assert mongo.db.name == 'test'
if __name__ == '__main__':
unittest.main()
This does not seem like the way to go at all. It also makes it impossible to use the app.test_client(). What is the proper way to instantiate the test database in a flask test setting?
I'm not sure how to do that via config files, but I have another approach that might interest you.
When I have been creating unit tests to my flask / mongo app, I have just simply read sys.args (in my '__init__.py') and decided from there if I want to use test DB or the actual DB, like this:
if "__init__.py" == sys.argv[0]:
db = LoginManager()
else:
db = TestDB() # if running unit tests
I don't know is this the most robust way to work with unit tests, but it is most certainly is super simple way to do it, and does the job.
If you are interested about the whole setup, check out my example app.
You will need to re-create the mongo object in each different unit test that needs to access a different database defined in your config. Here is how I did it and to get a real test I wrote a value to the database and made sure I could read it back two different ways:
import uuid
from datetime import datetime
from flask_testing import TestCase
from flask_pymongo import PyMongo
from project import app
class TestMongoDev(TestCase):
def create_app(self):
app.config.from_object('project.config.DevelopmentConfig')
return app
def test_mongo_development(self):
mongo = PyMongo(app)
testval = str(uuid.uuid4())
inserted_id = mongo.db.test.insert_one({
'testedAt': datetime.now(),
'testval': testval
}).inserted_id
self.assertTrue(mongo.db.name == 'dev')
self.assertTrue(mongo.db.test.find_one({'testval': testval})['testval'] == testval)
self.assertTrue(mongo.db.test.find_one({'_id': inserted_id})['testval'] == testval)
class TestMongoTest(TestCase):
def create_app(self):
app.config.from_object('project.config.TestingConfig')
return app
def test_mongo_testing(self):
mongo = PyMongo(app)
testval = str(uuid.uuid4())
inserted_id = mongo.db.test.insert_one({
'testedAt': datetime.now(),
'testval': testval
}).inserted_id
self.assertTrue(mongo.db.name == 'test')
self.assertTrue(mongo.db.test.find_one({'testval': testval})['testval'] == testval)
self.assertTrue(mongo.db.test.find_one({'_id': inserted_id})['testval'] == testval)
class TestMongoProd(TestCase):
def create_app(self):
app.config.from_object('project.config.ProductionConfig')
return app
def test_mongo_production(self):
mongo = PyMongo(app)
testval = str(uuid.uuid4())
inserted_id = mongo.db.test.insert_one({
'testedAt': datetime.now(),
'testval': testval
}).inserted_id
self.assertTrue(mongo.db.name == 'prod')
self.assertTrue(mongo.db.test.find_one({'testval': testval})['testval'] == testval)
self.assertTrue(mongo.db.test.find_one({'_id': inserted_id})['testval'] == testval)
Suppose I need to do before_request for each flask servers
How can I share the following snippet to each servers without COPY-PASTE
#app.before_request
def before_request(*args, **kwargs):
params = get_params()
if params.has_key('start_dt') and params.has_key('end_dt'):
g.mongo_query = Mongo.get_date_range_query(params)
else:
g.mongo_query = {}
You could use application factory for this. If you initialize your flask applications like so:
from flask import Flask
import yourdb
def create_app(config_filename):
app = Flask(__name__)
app.config.from_pyfile(config_filename)
yourdb.init_app(app)
#add_extensions
#add_blueprints/views
# ... some other configuration ...
#app.before_request
def before_request(*args, **kwargs):
#Your code
return app
From manage/run, you would then
from somewhere import create_app
app = create_app(<your_config>)