Router and router mixins

A router is an implementation of the abstract class Entity, that uses an entity store to allow routing other entities. In the code, this is represented by subclassing django_crucrudile.entities.store.EntityStore and django_crucrudile.entities.Entity, and providing a generator in patterns(), yielding URL patterns made from the entity store. Providing django_crucrudile.entities.Entity.patterns() makes router classes concrete implementations of the Entity abstract class, which allows them to be used in entity stores.

This module contains three implementations of routers, a simple one, and two implementations adapted to Django models :

Base router

class django_crucrudile.routers.Router(namespace=None, url_part=None, redirect=None, add_redirect=None, add_redirect_silent=None, get_redirect_silent=None, generic=None, **kwargs)[source]

Bases: django_crucrudile.entities.store.EntityStore, django_crucrudile.entities.Entity

RoutedEntity that yields an URL group containing URL patterns from the entities in the entity store (django_crucrudile.entities.store.EntityStore). The URL group can be set have an URL part and na namespace.

Also handles URL redirections : allows setting an Entity as “index”, which means that it will become the default routed entity for the parent entity (implementation details in get_redirect_pattern()).

Inheritance diagram of Router

redirect_max_depth = 100
Attribute redirect_max_depth:
 Max depth when following redirect attributes
__init__(namespace=None, url_part=None, redirect=None, add_redirect=None, add_redirect_silent=None, get_redirect_silent=None, generic=None, **kwargs)[source]

Initialize Router base attributes from given arguments

Parameters:
namespace = None
Attribute namespace:
 If defined, group this router’s patterns in an URL namespace
url_part = None
Attribute url_part:
 If defined, add to router URL (use when as regex when building URL group)
redirect = None
Attribute redirect:
 If defined, Router will add a redirect view to the returned patterns. To get the redirect target, get_redirect_pattern() will follow redirect attributes in the stored entities. The attribute’s value is altered by the register(), if index is True in its arguments or if the registered entity django_crucrudile.entities.Entity.index attribute is set to True.
add_redirect = None
Attribute add_redirect:
 Add redirect pattern when calling patterns(). If None (default), will be guessed using redirect (Add redirect only if there is one defined)
add_redirect_silent = False
Attribute add_redirect_silent:
 Fail silently when the patterns reader is asked to add the redirect patterns and the redirect attribute is not set (on self). Defaults to False, because in the default configuration, add_redirect is guessed using redirect, using bool. Set to True if you’re using add_redirect explicitly and want the redirect pattern to be optional.
get_redirect_silent = False
Attribute get_redirect_silent:
 Fail silently when following redirect attributes to find the redirect URL name (if no URL name is found).
generic = False
Attribute generic:
 If True, get_register_map() will return a model.generic.GenericModelRouter (with preconfigured Django videws) for the Model type.
get_register_map()[source]

Add two base register mappings (to the mappings returned by the super implementation)

returns:Register mappings
rtype:dict
register(entity, index=False, map_kwargs=None)[source]

Register routed entity, using django_crucrudile.entities.store.EntityStore.register()

Set as index when index or entity.index is True.

Parameters:
  • entity (django_crucrudile.entities.Entity) – Entity to register
  • index (bool) – Register as index (set redirect to entity
  • map_kwargs (dict) – Optional. Keyword arguments to pass to mapping value if entity gets transformed.
>>> from mock import Mock
>>> router = Router()
>>> entity = Mock()
>>> entity.index = False
>>>
>>> router.register(entity)
>>> router.redirect is None
True
>>> entity = Mock()
>>> entity.index = False
>>>
>>> router.register(entity, index=True)
>>> router.redirect is entity
True
>>> entity = Mock()
>>> entity.index = True
>>>
>>> router.register(entity)
>>> router.redirect is entity
True
get_redirect_pattern(namespaces=None, silent=None, redirect_max_depth=None)[source]

Compile the URL name to this router’s redirect path (found by following Router.redirect), and that return a lazy django.views.generic.RedirectView that redirects to this URL name

Parameters:
  • namespaces (list of str) – Optional. The list of namespaces will be used to get the current namespaces when building the redirect URL name. If not given an empty list will be used.
  • silent (bool) – Optional. See Router.get_redirect_silent
  • redirect_max_depth (int) – Optional. See Router.redirect_max_depth
Raises:
  • OverflowError – If the depth-first search in the graph made from redirect attributes reaches the depth in redirect_max_depth (to intercept graph cycles)
  • ValueError – If no redirect found when following redirect attributes, and silent mode is not enabled.
>>> from mock import Mock
>>> entity = Mock()
>>> entity.redirect.redirect = 'redirect_target'
>>>
>>> router = Router()
>>> router.register(entity)
>>>
>>> pattern = router.get_redirect_pattern()
>>>
>>> type(pattern).__name__
'RegexURLPattern'
>>> pattern.callback.__name__
'RedirectView'
>>> pattern._target_url_name
'redirect_target'
>>> from mock import Mock
>>> entity = Mock()
>>> entity.redirect.redirect = 'redirect_target'
>>>
>>> router = Router()
>>> router.register(entity)
>>>
>>> pattern = router.get_redirect_pattern(
...  namespaces=['ns1', 'ns2']
... )
>>> type(pattern).__name__
'RegexURLPattern'
>>> pattern.callback.__name__
'RedirectView'
>>> pattern._target_url_name
'ns1:ns2:redirect_target'
>>> entity = Mock()
>>> entity.redirect.redirect = entity
>>>
>>> router = Router()
>>> router.register(entity)
>>>
>>> router.get_redirect_pattern()
... 
Traceback (most recent call last):
  ...
OverflowError: Depth-first search reached its maximum (100)
depth, without returning a leaf item (string).Maybe the
redirect graph has a cycle ?
>>> entity = Mock()
>>> entity.__str__ = lambda x: 'mock redirect'
>>> entity.redirect = None
>>>
>>> router = Router()
>>> router.register(entity)
>>>
>>> router.get_redirect_pattern()
... 
Traceback (most recent call last):
  ...
ValueError: Failed following redirect attribute (mock
redirect) (last redirect found : mock redirect, redirect
value: None)) in Router
patterns(namespaces=None, add_redirect=None, add_redirect_silent=None)[source]

Read _store and yield a pattern of an URL group (with url part and namespace) containing entities’s patterns (obtained from the entity store), also yield redirect patterns where defined.

Parameters:
>>> from mock import Mock
>>> router = Router()
>>> pattern = Mock()
>>> entity_1 = Mock()
>>> entity_1.index = False
>>> entity_1.patterns = lambda *args: ['MockPattern1']
>>>
>>> router.register(entity_1)
>>>
>>> list(router.patterns())
[<RegexURLResolver <str list> (None:None) ^>]
>>> next(router.patterns()).url_patterns
['MockPattern1']
>>> entity_2 = Mock()
>>> entity_2.index = True
>>> entity_2.redirect = 'redirect_target'
>>> entity_2.patterns = lambda *args: ['MockPattern2']
>>>
>>> router.register(entity_2)
>>>
>>> list(router.patterns())
... 
[<RegexURLResolver <RegexURLPattern list> (None:None) ^>]
>>> next(router.patterns()).url_patterns
... 
[<RegexURLPattern redirect_target-redirect ^$>,
 'MockPattern1', 'MockPattern2']
>>> router.redirect = None
>>>
>>> list(router.patterns(add_redirect=True))
... 
Traceback (most recent call last):
  ...
ValueError: No redirect attribute set (and
``add_redirect_silent`` is ``False``).

Router mixins

Model router mixin

class django_crucrudile.routers.mixins.model.ModelMixin(model=None, url_part=None, **kwargs)[source]

Bases: builtins.object

ModelRouter with no views. Give model kwarg where needed, ask it in __init__(), and map SingleObjectMixin and MultipleObjectMixin to django_crucrudile.routes.ModelViewRoute in register functions.

Inheritance diagram of ModelMixin

__init__(model=None, url_part=None, **kwargs)[source]

Read model (from args or class-level value (model), fail if none found.

Parameters:model (django.db.Models) – see model
Raises ValueError:
 if model not passed an argument and not defined on class
model = None
Attribute model:
 Model used when building router URL name and URL part, and passed to registered routes. Must be defined at class-level or passed to __init__().
model_url_part[source]

Return the model name to be used when building the URL part

Returns:URL part from the Django model name (model._meta.model_name)
Return type:str
>>> from mock import Mock
>>>
>>> model = Mock()
>>> model._meta.model_name = 'testmodel'
>>> route = ModelMixin(model=model)
>>>
>>> route.model_url_part
'testmodel'
get_register_map_kwargs()[source]

Add model as kwarg when applying register map

Returns:Keyword arguments to pass to mapping value, when applying register map (from get_register_map()) in register()
Return type:dict

See also

For doctests that use this member, see django_crucrudile.routers.model.ModelRouter

get_base_store_kwargs()[source]

Add model so that the route classes in the base store will get the model as a kwarg when being instantiated

Returns:Keyword arguments to pass to mapping value, when applying class register map (from get_register_class_map()) in register_class()
Return type:dict

See also

For doctests that use this member, see django_crucrudile.routers.model.ModelRouter

get_register_map()[source]

Add django_crucrudile.routes.ModelViewRoute mapping for SingleObjectMixin and MultipleObjectMixin.

This mapping allows registering Django generic views in the base, making them register as django_crucrudile.routes.ModelViewRoute and instantiate with model.

Returns:Entity store mappings
Return type:dict

See also

For doctests that use this member, see django_crucrudile.routers.model.ModelRouter

classmethod get_register_class_map()[source]

Add django_crucrudile.routes.ModelViewRoute.make_for_view() mapping for SingleObjectMixin and MultipleObjectMixin.

This mapping allows registering Django generic views in the base store, so that entities made using django_crucrudile.routes.ModelViewRoute (and instantiated with model) get registered in the entity store when ModelMixin gets instantiated (in django_crucrudile.entities.store.EntityStore.register_base_store()).

django_crucrudile.routes.ModelViewRoute.make_for_view() creates a new django_crucrudile.routes.ModelViewRoute class, and uses its argument as the django_crucrudile.routes.ViewRoute.view_class attribute.

Returns:Base store mappings
Return type:dict

See also

For doctests that use this member, see django_crucrudile.routers.model.ModelRouter

Model router

class django_crucrudile.routers.model.ModelRouter(model=None, url_part=None, **kwargs)[source]

Bases: django_crucrudile.routers.mixins.model.ModelMixin, django_crucrudile.routers.Router

Model router, implements django_crucrudile.routers.mixins.model.ModelMixin`with :class:`django_crucrudile.routers.Router, to provide a Model that passes the model when instantiating entities.

Inheritance diagram of ModelRouter

>>> from django.views.generic.detail import SingleObjectMixin
>>> from mock import Mock
>>>
>>> class GenericView(SingleObjectMixin):
...   pass
>>> class NotGenericView:
...   pass
>>> model = Mock()
>>> view = Mock()
>>>
>>> model._meta.model_name = 'modelname'
>>>
>>> router = ModelRouter(model=model)
>>>
>>> router.model_url_part
'modelname'
>>> router.register(GenericView)

Generic model router

class django_crucrudile.routers.model.generic.GenericModelRouter(model=None, url_part=None, **kwargs)[source]

Bases: django_crucrudile.routers.model.ModelRouter

Generic model router, subclasses django_crucrudile.routers.model.ModelRouter and use 5 Django generic views for each registered model.

Provides specific django_crucrudile.routes.ModelViewRoute classes, created for the following Django generic views :

  • django.views.generic.ListView
  • django.views.generic.DetailView
  • django.views.generic.CreateView
  • django.views.generic.UpdateView
  • django.views.generic.DeleteView

These classes are registered in the base store, using django_crucrudile.entities.store.EntityStore.register_class() or the django_crucrudile.entities.store.provides() decorator. They will be instantiated (with the model as argument) when the router is itself instantiated, using django_crucrudile.entities.store.EntityStore.register_base_store().

Inheritance diagram of GenericModelRouter

>>> import tests.unit
>>> from django.db.models import Model
>>> from django_crucrudile.routers import Router, ModelRouter
>>>
>>> # needed to subclass Django Model
>>> __name__ = "tests.doctests"
>>>
>>> class TestModel(Model):
...   pass
>>> router = Router(generic=True)
>>>
>>> router.register(TestModel)
>>> print(router.get_str_tree())
... 
 - Router  @ ^
   - GenericModelRouter testmodel @ ^testmodel/
     - testmodel-list-redirect @ ^$ RedirectView
     - testmodel-delete @ ^delete$ DeleteView
     - testmodel-update @ ^update$ UpdateView
     - testmodel-create @ ^create$ CreateView
     - testmodel-detail @ ^detail$ DetailView
     - testmodel-list @ ^list$ ListView

The following graph may help to explain the relation between the generic views, routes and routers :