In my pyramid application I am trying to implement authorization by decorating the view function.
When I use the config.scan() function none of the views are added, however if I explicitly add them using config.add_view() everything works fine.
I have two file one which defines all the view functions (views.py)
from pyramid.view import view_config
from pyramid.response import Response
from functools import wraps
def authorized(func): #decorator difnition
#wraps(func)
def new_func(request):
if(request.cookies.get('user')): # authorization
return func(request)
else:
return Response('not authirised')
return new_func
#view_config(route_name='hello') # view function being decorated
#authorized
def privileged_action(request):
return Response('Hello %(name)s!' % request.matchdict)
And another file to create the server (serve.py) which imports views.py
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from views import privileged_action
if __name__ == '__main__':
config = Configurator()
config.add_route('hello', '/hello/{name}')
# config.add_view(privileged_action, route_name='hello') # This works
config.scan() # This doesn't work
app = config.make_wsgi_app()
server = make_server('0.0.0.0', 8080, app)
server.serve_forever()
This gives 404 not found error if I access using 'http://localhost:8080/hello/a'
Why does this not work?
Is there any way to make this work?
Your code with the decorators looks fine.
The documentation for Configurator.scan() states for its first argument:
The package argument should be a Python package or module object (or a dotted Python name which refers to such a package or module). If package is None, the package of the caller is used.
So make sure you are doingconfig.scan(views), to get your web app dynamically adding your views.
Related
I'm trying to create a website with optional url sub-paths:
/user - Returns general information on users
/user/edit - Edits the user
I've tried setting:
config.add_route('user', '/user/{action}')
#view_defaults(route_name="user")
class UserViews():
# not sure what (if anything) to put in #view_config here...
def user_general(self):
return Response("General User Info"
#view_config(match_param="action=edit")
def edit(self):
return Response("Editing user")
However while this works for /user/edit, it returns a 404 for /user
It also fails in the same way if I set 2 explicit routes with a shared path - e.g.:
config.add_route('login', '/user')
config.add_route('edit_user', '/user/edit')
I've tried things like setting match_params="action=" but can't get it to work.
Any ideas on how this can be achieved?
user_general inherits the default route configuration of the class, which requires an {action} match param. When you do not supply that in the request, the route for that view will never match, returning a 404 not found response.
You need to add a decorator with the route_name argument to user_general to override the default route for the view.
#view_config(
route_name="user"
)
def user_general(self):
The following works for me as a complete example with some minor explicit naming conventions.
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response
from pyramid.view import view_config, view_defaults
#view_defaults(route_name="user_action")
class UserViews():
def __init__(self, context, request):
self.request = request
self.context = context
#view_config(
route_name="user_get",
request_method="GET"
)
def get_user(request):
return Response("I got you, Babe!")
#view_config(
match_param="action=edit"
)
def edit(self):
return Response("Don't ever change, Babe!")
if __name__ == "__main__":
with Configurator() as config:
config.add_route("user_get", "/user")
config.add_route('user_action', '/user/{action}')
config.scan()
app = config.make_wsgi_app()
server = make_server("0.0.0.0", 6543, app)
server.serve_forever()
Flask-babel doesn't call its localeselector even once. I'm using app factory to init my app. The translations folder is within my app, It was created by babel, according to the docs. I've also tried moving the translations dir to the folder containing the run.py, which calls the factory, but to no effect.
from flask import Flask, session, request
from myapp.text_fields import next_month
from myapp.config import Config
from flask_babel import Babel
babel = Babel()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
babel.init_app(app)
from myapp.views import user
from myapp.errors import errors
app.register_blueprint(user)
app.register_blueprint(errors)
#babel.localeselector
def get_locale():
try:
language = session['language']
except KeyError:
language = None
if language is not None:
return language
return request.accept_languages.best_match(app.config['LANGUAGES'])
#app.context_processor
def inject_conf_var():
return dict(
MONTH=next_month(),
AVAILABLE_LANGUAGES=app.config['LANGUAGES'],
CURRENT_LANGUAGE=session.get('language',
request.accept_languages.best_match(app.config['LANGUAGES'])))
return app
Here's the config part concerning babel:
BABEL_DEFAULT_LOCALE = 'pl'
LANGUAGES = ['pl', 'ua', 'ru', 'en']
So far I've complied only EN, tried to change the default, but it doens't do anything either. Seems like babel is not able to locate the translations folder, I'm not sure how to fix this.
Solved it, turned out I had to move localeselector out of the factory. Since most of my text fields are generated on the service side rather than forntend, I also had to use lazy_text on all of them, as they are called by endpoints rather than defined within them.
I am running unittests in a Flask app and I keep getting 404 when views.py file is not imported even though it is not used. I have such tests.py package:
import unittest
from presence_analyzer import main, utils
from presence_analyzer import views
class PresenceAnalyzerViewsTestCase(unittest.TestCase):
def setUp(self):
self.client = main.app.test_client()
def test_mainpage(self):
resp = self.client.get('/')
self.assertEqual(resp.status_code, 302)
When I delete views import the described problem occurs. Views are organized in a similar way to this:
from presence_analyzer.main import app
#app.route('/')
def mainpage():
return redirect('/static/presence_weekday.html')
And the main.py file:
import os.path
from flask import Flask
app = Flask(__name__) # pylint: disable=invalid-name
app.config.update(
DEBUG=True,
)
I guess it's something similar to what happened in this case, so I'm trying to change the application so that I don't have to make this dumb imports while testing. I've been trying to make use of the answer from above, but still can't make it work and these docs don't seem helpful. What am I doing wrong? main.py:
from flask.blueprints import Blueprint
PROJECT_NAME = 'presence_analyzer'
blue_print = Blueprint(PROJECT_NAME, __name__)
def create_app():
app_to_create = Flask(__name__) # pylint: disable=invalid-name
app_to_create.register_blueprint(blue_print)
return app_to_create
app = create_app()
views.py:
from presence_analyzer.main import app, blue_print
#blue_print.route('/')
def mainpage():
return redirect('/static/presence_weekday.html')
And tests.py has remained unchanged.
You must import views, or the route will not be registered. No, you are not executing the views directly, but importing executes code all module-level code. Executing code calls route. route registers the view function. You cannot get around needing to import a module in order to use the module.
I would like to use the following basic authentication decorator in my blueprints:
def requires_auth(func):
#wraps(func)
def decorated(*args, **kwargs):
request_auth = request.authorization
if not request_auth or not auth.authenticate(request_auth.username, request_auth.password):
return api.response_auth_failed()
return func(*args, **kwargs)
return decorated
And in the blueprint:
#bp.route("/")
#requires_auth
def root():
return "root"
But it relies on the flask_peewee.auth module which also requires a db instance, which requires the app instance as the Database() module uses the app for configuration:
db = Database(app)
auth = Auth(app, db)
I had this working before when the application was simpler by instantiating all of this in the one app.py file, but now I'd like to organize it a little better.
Would it be possible to move my db.Model definitions, and the above requires_auth decorator to another file, say a db_models.py module, and import the requires_auth decorator into my individual Blueprint definitions? How can I do that if I need access to the app object to create the db object?
Edit: I've reorganized my app to look like this: http://charlesleifer.com/blog/structuring-flask-apps-a-how-to-for-those-coming-from-django/ I put the requires_auth function in my auth.py module an I can import and use it in the view.py but when I try and import it into one of my blueprints it fails. I think it's because auth module imports the app module, and the blueprint is extending the app module so I'm getting a circular import. Any ideas?
As you've noted, I think the problem is your module organization. What if you create a decorators module that contains requires_auth?
Then you will structure your imports to flow like:
app.py (instantiate app & db)
auth.py (instantiate auth, imports from app.py)
api.py (instantiate api, imports from app.py)
decorators.py (define requires_auth, imports from auth and api)
blueprints/views.py (imports from app.py, auth.py, api.py, decorators.py)
main.py (imports app, auth, api, decorators, blueprints) and is entry-point for application.
I want to build a REST web service on app engine. Currently i have this:
from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
class UsersHandler(webapp.RequestHandler):
def get(self, name):
self.response.out.write('Hello '+ name+'!')
def main():
util.run_wsgi_app(application)
#Map url like /rest/users/johnsmith
application = webapp.WSGIApplication([(r'/rest/users/(.*)',UsersHandler)]
debug=True)
if __name__ == '__main__':
main()
And i would like to retreive for example all my users when the path /rest/users is accessed. I Imagine I can do this by building another handler, but I want to know if is possible to do it inside of this handler.
Sure, you can -- change your handler's get method to
def get(self, name=None):
if name is None:
"""deal with the /rest/users case"""
else:
# deal with the /rest/users/(.*) case
self.response.out.write('Hello '+ name+'!')
and your application to
application = webapp.WSGIApplication([(r'/rest/users/(.*)', UsersHandler),
(r'/rest/users', UsersHandler)]
debug=True)
In other words, map your handler to all the URL patterns you want it to handle, and make sure the handler's get method can distinguish among them easily (typically via its arguments).