PCv5/app/utils/converters.py

81 lines
2.5 KiB
Python

"""
utils.converter: Custom URL converters to match patterns in @app.route()
The Flask documentation is elusive on this topic. To add a new converter,
proceed as follows:
1. Define a new converter class.
2. Set the [regex] attribute to decide which portion of the URL will be
considered for conversion (apparently the default is everything until next
slash or end of string).
3. Define the to_python() and to_url() methods to actually convert.
4. Add the class to __all__ at the bottom of this file.
5. In app/__init__.py, add a dictionary entry to [app.url_map.converters].
For more information, see the Werkzeug documentation:
<https://werkzeug.palletsprojects.com/en/0.15.x/routing/#custom-converters>
"""
from werkzeug.routing import BaseConverter, ValidationError
from app.models.forum import Forum
from app.models.topic import Topic
from slugify import slugify
class ForumConverter(BaseConverter):
# This regex will decide which portion of the URL is matched by the curtom
# converter. By default, slashes are not included, so we must add them.
regex = r'[a-z/]+'
def to_python(self, url):
url = '/' + url
f = Forum.query.filter_by(url=url).first()
if f is None:
raise ValidationError(f"ForumConverter: no forum with url {url}")
return f
def to_url(self, forum):
return forum.url[1:]
class PageConverter(BaseConverter):
# Matches integers for the topic number, then either nothing, a page
# number, a slug, or a page number followed by a slug
regex = r'(\d+)(?:/(\d+)|/fin)?(?:/[\w-]+)?'
object = None
def to_python(self, url):
tid, *args = url.split('/')
tid = int(tid)
if len(args) == 0:
page = 1
elif args[0] == "fin":
page = -1
else:
# The argument is considered to be a slug if it's not "last" and it
# doesn't convert to an int
try:
page = int(args[0])
except ValueError:
page = 1
t = self.object.query.filter_by(id=tid).first()
if t is None:
raise Exception(f"BaseConverter: no object with id {url}")
return (t, page)
def to_url(self, object_and_page):
o, page = object_and_page
page = str(page) if page != -1 else "fin"
slug = slugify(o.title)
return f'{o.id}/{page}/{slug}'
class TopicPageConverter(PageConverter):
object = Topic
# Export only the converter classes
__all__ = "ForumConverter TopicPageConverter".split()