Source code for duck.routes.route_blueprint
"""
Module for route arrangements using Blueprint. This acts as a set of routes, much like how Flask's blueprints organize routes in a module.
Example:
```py
app = Blueprint(location=__file__, name="products", urlpatterns=...)
```
``` {note}
To resolve a url for a blueprint, the name should be `{blueprint_name}.{path_name}` .e.g.,
`"api.followers"` for blueprint named `api` and a path registered with name `followers`
```
"""
import pathlib
from typing import List, Optional
from duck.urls import (
URLPattern,
path,
re_path,
)
from duck.exceptions.all import BlueprintError
from duck.utils.path import joinpaths
[docs]
class Blueprint:
"""
Mini application for storing route information.
"""
__names = []
"""
Names of all created blueprints.
"""
def __init__(
self,
location: str,
name: str,
urlpatterns: Optional[List[URLPattern]] = None,
prepend_name_to_urls: bool = True,
static_dir: str = "static",
template_dir: str = "templates",
enable_static_dir: bool = True,
enable_template_dir: bool = True,
is_builtin: bool = False,
):
"""
Initialize the Blueprint.
Args:
location (str): The absolute path to where the blueprint is located.
name (str): A valid string representing the blueprint's name.
urlpatterns (Optional[List[URLPattern]]): List of urlpatterns created using duck.urls.path or re_path.
prepend_name_to_urls (bool): Whether to prepend name to urlpatterns. Defaults to True.
static_dir (str): The location of static files within the blueprint base directory.
template_dir (str): The template directory for the blueprint.
enable_static_dir (bool): Boolean on whether to enable commands like `duck collectstatic` to access the blueprint staticfiles.
enable_template_dir (bool): Expose the template dir for template resolving.
is_builtin (bool): Flag the route blueprint as an internal builtin blueprint, Defaults to False (optional).
"""
if not (name and isinstance(name, str)):
raise BlueprintError(
"Name for the Blueprint must be a valid string"
)
elif name in self.__names:
raise BlueprintError(
f'Blueprint with the same name already exists. Conflicting name: "{name}".'
)
else:
type(self).__names.append(name)
self.location = location
self.name = name
self.urlpatterns = []
self.prepend_name_to_urls = prepend_name_to_urls
self.static_dir = static_dir
self.template_dir = template_dir
self.enable_static_dir = enable_static_dir
self.enable_template_dir = enable_template_dir
self.is_builtin = is_builtin
urlpatterns = urlpatterns or []
try:
for urlpattern in urlpatterns:
self.add_urlpattern(urlpattern)
except Exception as e:
raise BlueprintError(
"Error while trying to add urlpatterns provided, ensure all urlpatterns are in correct format."
) from e
[docs]
def __repr__(self):
return f'<{self.__class__.__name__} name="{self.name}", location=...>'
@property
def root_directory(self) -> str:
"""
Returns the absolute blueprint root path.
"""
root = str(pathlib.Path(self.location).parent)
return root
@property
def root_directory_name(self) -> str:
"""
Returns the blueprint root path name.
"""
location = pathlib.Path(self.location)
relative_root = location.relative_to(location.parent)
return relative_root.name
[docs]
def add_urlpattern(
self,
urlpattern: URLPattern,
):
"""
Adds a url pattern to the blueprint urlpatterns collection.
Notes:
- This reconfigures the urlpattern to belong to the blueprint before adding
the url pattern.
"""
name = urlpattern["name"]
route = urlpattern["url"]
if self.prepend_name_to_urls:
route = joinpaths(self.name, route)
if name:
name = f"{self.name}.{name}"
else:
name = f"{self.name}.route_{len(self.urlpatterns) + 1}" # Auto-generate names
urlpattern["url"] = route
urlpattern["name"] = name
self.urlpatterns.append(urlpattern)