Skip to content

Instantly share code, notes, and snippets.

@dahlia
Created October 30, 2012 03:24
Show Gist options
  • Save dahlia/3978135 to your computer and use it in GitHub Desktop.
Save dahlia/3978135 to your computer and use it in GitHub Desktop.
Building URLs within model objects
""":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