"""This module contains :class:`ModelMixin`, a route mixin that can be used
to bind a model to a route, and use it when computing route metadata.
"""
[docs]class ModelMixin:
"""Route mixin that requires a model to be set either on the class
(:attr:`model` attribute), or to be passed in :func:`__init__`,
and provides the URL name and URL part using the model metadata.
.. warning::
This mixin does not make
:class:`django_crucrudile.routes.base.BaseRoute` a concrete
class !
.. inheritance-diagram:: ModelMixin
"""
model = None
"""
:attribute model: Model to use on the Route
:type model: :class:`django.db.models.Model`
"""
prefix_url_part = False
"""
:attribute prefix_url_part: Prefix the URL part with the model
(ex: ``/model/<url_part>``)
:type prefix_url_part: bool
"""
[docs] def __init__(self,
*args,
model=None,
**kwargs):
"""Initialize ModelRoute, check that model is defined at class-level
or passed as argument.
:argument model: See :attr:`model`
:raises ValueError: ``model`` argument is None, and no model
defined in :attr:`model`
"""
if model is not None:
self.model = model
elif self.model is None: # pragma: no cover
raise ValueError(
"No ``model`` argument provided to __init__"
", and no model defined as class attribute (in {})"
"".format(self)
)
super().__init__(*args, **kwargs)
@property
[docs] def model_url_name(self):
"""Return the model name to be used when building the URL name
:returns: URL name from model name, using Django internals
:rtype: str
>>> from django_crucrudile.routes.base import BaseRoute
>>> from mock import Mock
>>>
>>> class ModelRoute(ModelMixin, BaseRoute):
... def get_callback(self):
... pass
>>>
>>> model = Mock()
>>> model._meta.model_name = 'testmodel'
>>> route = ModelRoute(model=model, name='routename')
>>>
>>> route.model_url_name
'testmodel'
"""
return self.model._meta.model_name
@property
[docs] def model_url_part(self):
"""Return the model name to be used when building the URL part
:returns: URL part from the URL name (:func:`model_url_name`)
:rtype: str
>>> from django_crucrudile.routes.base import BaseRoute
>>> from mock import Mock
>>>
>>> model = Mock()
>>> model._meta.model_name = 'testmodel'
>>> route = ModelMixin(model=model)
>>>
>>> route.model_url_part
'testmodel'
"""
return self.model._meta.model_name
[docs] def get_url_specs(self):
"""Return URL specs where the model URL name is appended to the prefix
part list if needed
:returns: URL specifications
:rtype: iterable of 3-tuple
>>> from django_crucrudile.routes.base import BaseRoute
>>> from mock import Mock
>>>
>>> class ModelRoute(ModelMixin, BaseRoute):
... def get_callback(self):
... pass
>>>
>>> model = Mock()
>>> model._meta.model_name = 'testmodel'
>>> route = ModelRoute(model=model, name='routename')
>>>
>>> list(route.get_url_specs())
[([], ['routename'], [])]
With :attr:`prefix_url_part` set to ``True`` :
>>> from django_crucrudile.routes.base import BaseRoute
>>> from mock import Mock
>>>
>>> class PrefixModelRoute(ModelMixin, BaseRoute):
... def get_callback(self):
... pass
... prefix_url_part = True
>>>
>>> model = Mock()
>>> model._meta.model_name = 'testmodel'
>>> route = PrefixModelRoute(
... model=model, name='routename',
... )
>>>
>>> list(route.get_url_specs())
... # doctest: +NORMALIZE_WHITESPACE
[(['testmodel'],
['routename'],
[])]
"""
for prefix, name, suffix in super().get_url_specs():
if self.prefix_url_part:
yield prefix + [self.model_url_part], name, suffix
else:
yield prefix, name, suffix
[docs] def get_url_name(self):
"""Return the URL name built :func:`model_url_name` and
:attr:`Route.name`.
:returns: compiled URL name
:rtype: str
>>> from django_crucrudile.routes.base import BaseRoute
>>> from mock import Mock
>>>
>>> class ModelRoute(ModelMixin, BaseRoute):
... def get_callback(self):
... pass
>>>
>>> model = Mock()
>>> model._meta.model_name = 'testmodel'
>>> route = ModelRoute(
... model=model, name='routename',
... )
>>>
>>> route.get_url_name()
'testmodel-routename'
"""
return "{}-{}".format(self.model_url_name, self.name)