diff --git a/flask/views.py b/flask/views.py index 848ccb0b..b3027970 100644 --- a/flask/views.py +++ b/flask/views.py @@ -51,6 +51,9 @@ class View(object): #: A list of methods this view can handle. methods = None + #: Setting this disables or force-enables the automatic options handling. + provide_automatic_options = None + #: The canonical way to decorate class-based views is to decorate the #: return value of as_view(). However since this moves parts of the #: logic from the class declaration to the place where it's hooked @@ -99,6 +102,7 @@ class View(object): view.__doc__ = cls.__doc__ view.__module__ = cls.__module__ view.methods = cls.methods + view.provide_automatic_options = cls.provide_automatic_options return view diff --git a/tests/test_views.py b/tests/test_views.py index 65981dbd..896880c0 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -109,6 +109,43 @@ def test_view_decorators(): assert rv.headers['X-Parachute'] == 'awesome' assert rv.data == b'Awesome' +def test_view_provide_automatic_options_attr(): + app = flask.Flask(__name__) + + class Index1(flask.views.View): + provide_automatic_options = False + def dispatch_request(self): + return 'Hello World!' + + app.add_url_rule('/', view_func=Index1.as_view('index')) + c = app.test_client() + rv = c.open('/', method='OPTIONS') + assert rv.status_code == 405 + + app = flask.Flask(__name__) + + class Index2(flask.views.View): + methods = ['OPTIONS'] + provide_automatic_options = True + def dispatch_request(self): + return 'Hello World!' + + app.add_url_rule('/', view_func=Index2.as_view('index')) + c = app.test_client() + rv = c.open('/', method='OPTIONS') + assert sorted(rv.allow) == ['OPTIONS'] + + app = flask.Flask(__name__) + + class Index3(flask.views.View): + def dispatch_request(self): + return 'Hello World!' + + app.add_url_rule('/', view_func=Index3.as_view('index')) + c = app.test_client() + rv = c.open('/', method='OPTIONS') + assert 'OPTIONS' in rv.allow + def test_implicit_head(): app = flask.Flask(__name__)