Create the RoutedMixin class

It will be used both in the app, and later blueprint classes.
This commit is contained in:
Gergely Polonkai 2018-07-17 15:00:56 +02:00
parent 89dc258a5b
commit 8d45611e35
2 changed files with 67 additions and 43 deletions

View File

@ -25,6 +25,8 @@ from flask_babelex import Babel, get_locale as babel_get_locale
from flask_security import SQLAlchemyUserDatastore, current_user, login_required from flask_security import SQLAlchemyUserDatastore, current_user, login_required
from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
from calsocial.utils import RoutedMixin
def get_locale(): def get_locale():
"""Locale selector """Locale selector
@ -53,22 +55,7 @@ def template_vars():
} }
def route(*args, **kwargs): class CalendarSocialApp(Flask, RoutedMixin):
"""Mark a function as a future route
Such functions will be iterated over when the application is initialised. ``*args`` and
``**kwargs`` will be passed verbatim to `Flask.route()`.
"""
def decorator(func): # pylint: disable=missing-docstring
setattr(func, 'routing', (args, kwargs))
return func
return decorator
class CalendarSocialApp(Flask):
"""The Calendar.social app """The Calendar.social app
""" """
@ -101,18 +88,7 @@ class CalendarSocialApp(Flask):
self.context_processor(template_vars) self.context_processor(template_vars)
for attr_name in self.__dir__(): RoutedMixin.register_routes(self)
attr = getattr(self, attr_name)
if not callable(attr):
continue
args, kwargs = getattr(attr, 'routing', (None, None))
if args is None:
continue
self.route(*args, **kwargs)(attr)
self.before_request(self.goto_first_steps) self.before_request(self.goto_first_steps)
@ -165,7 +141,7 @@ class CalendarSocialApp(Flask):
return GregorianCalendar(timestamp.timestamp()) return GregorianCalendar(timestamp.timestamp())
@route('/about') @RoutedMixin.route('/about')
def about(self): def about(self):
"""View for the about page """View for the about page
""" """
@ -190,7 +166,7 @@ class CalendarSocialApp(Flask):
user_count=user_count, user_count=user_count,
event_count=event_count) event_count=event_count)
@route('/') @RoutedMixin.route('/')
def hello(self): def hello(self):
"""View for the main page """View for the main page
@ -206,7 +182,7 @@ class CalendarSocialApp(Flask):
return render_template('index.html', calendar=calendar, user_only=True) return render_template('index.html', calendar=calendar, user_only=True)
@staticmethod @staticmethod
@route('/register', methods=['POST', 'GET']) @RoutedMixin.route('/register', methods=['POST', 'GET'])
def register(): def register():
"""View for user registration """View for user registration
@ -236,7 +212,7 @@ class CalendarSocialApp(Flask):
return render_template('registration.html', form=form) return render_template('registration.html', form=form)
@staticmethod @staticmethod
@route('/new-event', methods=['GET', 'POST']) @RoutedMixin.route('/new-event', methods=['GET', 'POST'])
@login_required @login_required
def new_event(): def new_event():
"""View for creating a new event """View for creating a new event
@ -261,7 +237,7 @@ class CalendarSocialApp(Flask):
return render_template('event-edit.html', form=form) return render_template('event-edit.html', form=form)
@staticmethod @staticmethod
@route('/settings', methods=['GET', 'POST']) @RoutedMixin.route('/settings', methods=['GET', 'POST'])
@login_required @login_required
def settings(): def settings():
"""View for user settings """View for user settings
@ -282,7 +258,7 @@ class CalendarSocialApp(Flask):
return render_template('user-settings.html', form=form) return render_template('user-settings.html', form=form)
@staticmethod @staticmethod
@route('/event/<string:event_uuid>', methods=['GET', 'POST']) @RoutedMixin.route('/event/<string:event_uuid>', methods=['GET', 'POST'])
def event_details(event_uuid): def event_details(event_uuid):
"""View to display event details """View to display event details
""" """
@ -309,7 +285,7 @@ class CalendarSocialApp(Flask):
return render_template('event-details.html', event=event, form=form) return render_template('event-details.html', event=event, form=form)
@staticmethod @staticmethod
@route('/profile/@<string:username>') @RoutedMixin.route('/profile/@<string:username>')
def display_profile(username): def display_profile(username):
"""View to display profile details """View to display profile details
""" """
@ -324,7 +300,7 @@ class CalendarSocialApp(Flask):
return render_template('profile-details.html', profile=profile) return render_template('profile-details.html', profile=profile)
@staticmethod @staticmethod
@route('/profile/@<string:username>/follow') @RoutedMixin.route('/profile/@<string:username>/follow')
@login_required @login_required
def follow_user(username): def follow_user(username):
"""View for following a user """View for following a user
@ -345,7 +321,7 @@ class CalendarSocialApp(Flask):
return redirect(url_for('display_profile', username=username)) return redirect(url_for('display_profile', username=username))
@staticmethod @staticmethod
@route('/notifications') @RoutedMixin.route('/notifications')
def notifications(): def notifications():
"""View to list the notifications for the current user """View to list the notifications for the current user
""" """
@ -360,7 +336,7 @@ class CalendarSocialApp(Flask):
return render_template('notifications.html', notifs=notifs) return render_template('notifications.html', notifs=notifs)
@staticmethod @staticmethod
@route('/accept/<int:invite_id>') @RoutedMixin.route('/accept/<int:invite_id>')
def accept_invite(invite_id): def accept_invite(invite_id):
"""View to accept an invitation """View to accept an invitation
""" """
@ -390,7 +366,7 @@ class CalendarSocialApp(Flask):
return redirect(url_for('event_details', event_uuid=invitation.event.event_uuid)) return redirect(url_for('event_details', event_uuid=invitation.event.event_uuid))
@staticmethod @staticmethod
@route('/first-steps', methods=['GET', 'POST']) @RoutedMixin.route('/first-steps', methods=['GET', 'POST'])
@login_required @login_required
def first_steps(): def first_steps():
"""View to set up a new registrants profile """View to set up a new registrants profile
@ -417,7 +393,7 @@ class CalendarSocialApp(Flask):
return render_template('first-steps.html', form=form) return render_template('first-steps.html', form=form)
@staticmethod @staticmethod
@route('/edit-profile', methods=['GET', 'POST']) @RoutedMixin.route('/edit-profile', methods=['GET', 'POST'])
@login_required @login_required
def edit_profile(): def edit_profile():
"""View for editing ones profile """View for editing ones profile
@ -438,7 +414,7 @@ class CalendarSocialApp(Flask):
return render_template('profile-edit.html', form=form) return render_template('profile-edit.html', form=form)
@staticmethod @staticmethod
@route('/all-events') @RoutedMixin.route('/all-events')
def all_events(): def all_events():
"""View for listing all available events """View for listing all available events
""" """
@ -455,7 +431,7 @@ class CalendarSocialApp(Flask):
return render_template('index.html', calendar=calendar, user_only=False) return render_template('index.html', calendar=calendar, user_only=False)
@staticmethod @staticmethod
@route('/follow-requests') @RoutedMixin.route('/follow-requests')
@login_required @login_required
def follow_requests(): def follow_requests():
"""View for listing follow requests """View for listing follow requests
@ -470,7 +446,7 @@ class CalendarSocialApp(Flask):
return render_template('follow-requests.html', requests=requests) return render_template('follow-requests.html', requests=requests)
@staticmethod @staticmethod
@route('/follow-request/<int:follower_id>/accept') @RoutedMixin.route('/follow-request/<int:follower_id>/accept')
@login_required @login_required
def accept_follow(follower_id): def accept_follow(follower_id):
"""View for accepting a follow request """View for accepting a follow request

View File

@ -68,3 +68,51 @@ def force_locale(locale):
babel.locale_selector_func = orig_locale_selector_func babel.locale_selector_func = orig_locale_selector_func
for key, value in orig_attrs.items(): for key, value in orig_attrs.items():
setattr(ctx, key, value) setattr(ctx, key, value)
class RoutedMixin:
"""Mixin to lazily register class methods as routes
Works both for `Flask` and `Blueprint` objects.
Example::
class MyBlueprint(Blueprint, RoutedMixin):
def __init__(self, *args, **kwargs):
do_whatever_you_like()
RoutedMixin.register_routes(self)
@RoutedMixin.route('/')
def index(self):
return 'Hello, World!'
"""
def register_routes(self):
for attr_name in self.__dir__():
attr = getattr(self, attr_name)
if not callable(attr):
continue
args, kwargs = getattr(attr, 'routing', (None, None))
if args is None:
continue
self.route(*args, **kwargs)(attr)
@staticmethod
def route(*args, **kwargs):
"""Mark a function as a future route
Such functions will be iterated over when the application is initialised. ``*args`` and
``**kwargs`` will be passed verbatim to `Flask.route()`.
"""
def decorator(func): # pylint: disable=missing-docstring
setattr(func, 'routing', (args, kwargs))
return func
return decorator