Created
October 30, 2012 03:24
-
-
Save dahlia/3978135 to your computer and use it in GitHub Desktop.
Building URLs within model objects
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""":mod:`crosspop.web.urlmap` --- URL--model objects mapping | |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
The most of model objects (e.g. :class:`~crosspop.user.User`, | |
:class:`~crosspop.comic.Comic`) have their own permalink urls. | |
Although you can link them using :func:`flask.url_for()`:: | |
url_for('user.profile', login=user.login) | |
but this way requires you to remember endpoint (view function) | |
names (e.g. ``'user.profile'``) and parameters of them. | |
Instead, by using :func:`permalink()` function provided by | |
this module, you can link any model objects in the same integrated | |
way:: | |
permalink(user) | |
``|permalink`` --- Template filter | |
---------------------------------- | |
In Jinja2 templates you can use ``|permalink`` filter: | |
.. sourcecode:: jinja | |
{{ user|permalink }} | |
There's ``absolute`` option as well: | |
.. sourcecode:: jinja | |
{{ user|permalink(absolute=True) }} | |
.. note:: | |
This template filter is registered by :func:`setup_template_filter()` | |
function, and it would be automatically called. | |
Map view functions to objects | |
----------------------------- | |
When you faced this error on call of :func:`permalink()`: | |
.. sourcecode:: pytb | |
Traceback (most recent call last): | |
File "...", line 123, in somefunc | |
permalink(your_object) | |
File "/.../crosspop/web/urlmap.py", line 31, in permalink | |
'{2}.{3} type'.format(e, __name__, cls.__module__, cls.__name__) | |
TypeError: <YourObject xyz> does not match for the function 'get_url' | |
you probably did not implement crosspop.web.urlmap.get_url() | |
for YourObject type | |
the type of a passed object has to be mapped to a view function. | |
To map model classes to view functions, you have to extend | |
:func:`get_url()` generic method:: | |
@get_url.of(User) | |
def get_url(value): | |
return 'user.profile', {'login': value.login} | |
Its signature has to be: | |
.. function:: get_url(value) | |
Give the arguments to be passed to :func:`flask.url_for()` | |
function. | |
:param value: a model object to get its permalink | |
:returns: the pair of (endpoint, values). for example, | |
returning ``('user.profile', {'login': 'dahlia'})`` | |
means ``flask.url_for('user.profile', login='dahlia')``. | |
or it also can be treated as an url if it's just a string | |
:rtype: :class:`tuple`, :class:`basestring` | |
.. seealso:: | |
TypeQuery_ | |
It provides a simple and dirty way to define generic methods | |
to existing types. You can make overloaded methods using this. | |
.. _TypeQuery: http://pypi.python.org/pypi/TypeQuery | |
Permalink API | |
------------- | |
""" | |
from flask import url_for | |
from typequery import GenericMethod | |
from ..comic import Chapter, Comic, Sequence | |
from ..image.entity import Image, ImageSet | |
from ..user import User | |
__all__ = 'get_url', 'permalink', 'setup_template_filter' | |
def permalink(value, absolute=False): | |
"""Find the permalink of the given ``value``. Usually ``value`` is | |
a model object like :class:`~crosspop.user.User`. | |
:param value: the model object to find its url | |
:param absolute: returns the absolute url instead of the relative | |
path. default is ``False`` | |
:type absolute: :class:`bool` | |
:returns: the permalink of the given ``value`` | |
:rtype: :class:`basestring` | |
""" | |
cls = type(value) | |
try: | |
values = get_url(value) | |
except TypeError as e: | |
raise TypeError( | |
'{0!s}\nyou probably did not implement {1}.get_url() for ' | |
'{2}.{3} type'.format(e, __name__, cls.__module__, cls.__name__) | |
) | |
if isinstance(values, basestring): | |
return values | |
try: | |
endpoint, kwargs = values | |
except ValueError as e: | |
raise ValueError( | |
'{0!s}\n{1}.get_url() for {2}.{3} must return an url string of ' | |
'a pair (endpoint, keywords)'.format(e, __name__, cls.__module__, | |
cls.__name__) | |
) | |
return url_for(endpoint, _external=absolute, _method='GET', **kwargs) | |
def setup_template_filter(app): | |
"""Registers ``|permalink`` template filter. It makes templates | |
able to use ``|permalink`` template filters: | |
.. sourcecode:: jinja | |
{{ user|permalink }} | |
:param app: Flask app to setup | |
:type app: :class:`flask.Flask` | |
""" | |
app.jinja_env.filters['permalink'] = permalink | |
get_url = GenericMethod('get_url') | |
@get_url.of(Image) | |
@get_url.of(ImageSet) | |
def get_url(value): | |
return value.locate() | |
@get_url.of(User) | |
def get_url(value): | |
return 'user.profile', {'login': value.login} | |
@get_url.of(Comic) | |
def get_url(value): | |
return 'comic.comic', {'comic_id': value.id} | |
@get_url.of(Chapter) | |
def get_url(value): | |
return 'comic.chapter', { | |
'comic_id': value.comic_id, | |
'chapter_number': value.number | |
} | |
@get_url.of(Sequence) | |
def get_url(value): | |
chapter = value.chapter | |
return 'comic.sequence', { | |
'comic_id': chapter.comic_id, | |
'chapter_number': chapter.number, | |
'sequence_number': value.number | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment