Advanced Usage Guide¶
This guide covers advanced features and patterns for using django-brick-astley.
Type Validation¶
Brick kwargs are validated against their type hints. The behavior depends on your
DEBUG setting:
DEBUG=True: Type mismatches raise
BrickValidationErrorDEBUG=False: Type mismatches log a warning but allow rendering to continue
Supported Types¶
from brickastley import Brick, register
@register
class MyBrick(Brick):
# Basic types
name: str
count: int
price: float
active: bool
# Optional types (can be None)
subtitle: str | None = None
# Union types
value: str | int = 0
# With defaults
variant: str = "default"
Template Tag Values¶
In templates, values are parsed as follows:
{# Strings - use quotes #}
{% mybrick name="Hello World" %}
{% mybrick name='Single quotes work too' %}
{# Integers #}
{% mybrick count=42 %}
{% mybrick count=-5 %}
{# Floats #}
{% mybrick price=19.99 %}
{# Booleans #}
{% mybrick active=True %}
{% mybrick active=False %}
{# None #}
{% mybrick subtitle=None %}
{# Template variables #}
{% mybrick name=user.username %}
{% mybrick count=items|length %}
Extra Kwargs¶
Any kwargs passed to a brick that aren’t defined in the class are collected
in extra, a dictionary available in the template context. This is
useful for passing HTML attributes like class, id, data-*, or
aria-*, or any other custom data:
@register
class Button(Brick):
label: str
variant: str = "primary"
# Note: no class, id, or data-* defined
Use in template with extra kwargs:
{% button label="Click me" class="mt-4" id="submit-btn" data_action="submit" %}
The attrs Filter¶
Use the attrs filter to render extra kwargs as an HTML attributes string:
{# bricks/button.html #}
<button class="btn btn-{{ variant }}"{{ extra|attrs }}>
{{ label }}
</button>
This renders as:
<button class="btn btn-primary" class="mt-4" id="submit-btn" data-action="submit">
Click me
</button>
The attrs filter:
Converts underscores to hyphens (
data_action→data-action)Renders boolean
Trueas the attribute name (disabled=True→disabled="disabled")Skips
FalseandNonevaluesReturns a safe string with a leading space (so
{{ extra|attrs }}works directly after a tag name)
Merging Extra with Existing Attributes¶
To merge extra with your own classes, access individual items:
{# bricks/button.html #}
<button
class="btn btn-{{ variant }}{% if extra.class %} {{ extra.class }}{% endif %}"
{% if extra.id %}id="{{ extra.id }}"{% endif %}
{{ extra|attrs }}
>
{{ label }}
</button>
Note
When using |attrs, be aware that class and id will be rendered
again if present in extra. For complete control, iterate manually
or filter out specific keys in your get_context_data() method.
Context Inheritance¶
By default, bricks are isolated from the parent template context. This prevents unwanted variable shadowing where parent context variables could override brick parameters.
For example:
@register
class MyBrick(Brick):
name: str
class_: str | None = None # Optional parameter
{# Parent template has 'class' in context #}
{{ "parent-class" as class }}
{# Brick is isolated - 'class_' remains None #}
{% mybrick name="test" %}
Inheriting Specific Variables¶
If your brick needs access to parent context variables like request or user,
use the inherit_context class attribute to explicitly declare which variables
to inherit:
@register
class UserGreeting(Brick):
inherit_context = ("request", "user") # Inherit these from parent
greeting: str = "Hello"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# Now 'request' and 'user' are available
context["username"] = context.get("user").username if context.get("user") else "Guest"
return context
{# Parent template #}
{% user_greeting %} {# Can access request and user from parent #}
Note
If
inherit_contextisNone(default), no parent context is inheritedIf
inherit_contextis a list or tuple of strings, only those variables are inheritedBrick’s own context always overrides inherited values
This isolation prevents bugs where parent template variables unintentionally set optional brick parameters.
Custom Context Data¶
Override get_context_data() to add computed values or transform kwargs:
@register
class PriceTag(Brick):
amount: float
currency: str = "USD"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# Add computed values
context["formatted_price"] = f"{self.currency} {self.amount:.2f}"
context["is_free"] = self.amount == 0
return context
Use in template:
{# bricks/price_tag.html #}
<span class="price{% if is_free %} price--free{% endif %}">
{% if is_free %}
Free
{% else %}
{{ formatted_price }}
{% endif %}
</span>
Media (CSS and JavaScript)¶
Bricks support Django’s Media class for declaring CSS and JavaScript dependencies:
@register
class FancyButton(Brick):
label: str
class Media:
css = {
"all": ["css/fancy-button.css"],
}
js = ["js/fancy-button.js"]
You can access the media on brick instances:
button = FancyButton(label="Click")
print(button.media) # Outputs CSS and JS tags
Note
Currently, media assets must be included manually in your base template. Automatic collection of brick media is planned for a future release.
Custom Brick Names¶
Use the name parameter to customize the template tag name:
@register(name="btn")
class Button(Brick):
label: str
@register(name="cta")
class CallToAction(Brick):
text: str
url: str
Use in templates:
{% btn label="Click me" %}
{% cta text="Sign up now" url="/signup/" %}
Custom Template Paths¶
Set template_name to use a custom template location:
@register
class Button(Brick):
label: str
template_name = "components/buttons/primary.html"
@register
class Card(BlockBrick):
title: str
template_name = "ui/cards/basic.html"
Brick Inheritance¶
Bricks can inherit from other bricks:
class BaseButton(Brick):
label: str
disabled: bool = False
@register
class PrimaryButton(BaseButton):
# Inherits label and disabled
pass
@register
class DangerButton(BaseButton):
# Inherits label and disabled, adds confirm
confirm: bool = False
Note
Only bricks decorated with @register become template tags.
Base classes without the decorator are not registered.
Accessing the Registry¶
You can programmatically access registered bricks:
from brickastley.registry import get_registry, get_brick
# Get all registered bricks
registry = get_registry()
for name, brick_class in registry.items():
print(f"{name}: {brick_class}")
# Get a specific brick by name
ButtonClass = get_brick("button")
if ButtonClass:
button = ButtonClass(label="Hello")
Nesting Bricks¶
Bricks can be nested inside block bricks:
{% card title="User Actions" %}
<div class="button-group">
{% button label="Edit" variant="primary" %}
{% button label="Delete" variant="danger" %}
</div>
{% endcard %}
Template Tag Loading¶
By default, you need to load the brickastley template tags:
{% load brickastley %}
To make bricks available in all templates without loading, add to your settings:
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"OPTIONS": {
"builtins": [
"brickastley.templatetags.brickastley",
],
},
},
]
Autodiscovery¶
Brickastley automatically discovers bricks.py modules in all installed Django apps
when the app is ready. This happens in the AppConfig.ready() method.
The autodiscovery process:
Iterates through all installed apps
Checks if each app has a
bricks.pymoduleImports the module, triggering
@registerdecoratorsRegisters all discovered bricks as template tags
If you need to manually trigger discovery (e.g., in tests):
from brickastley.autodiscover import autodiscover
from brickastley.templatetags.brickastley import register_brick_tags
autodiscover()
register_brick_tags()
Testing Bricks¶
Test brick instantiation and context:
from myapp.bricks import Button
def test_button_defaults():
button = Button(label="Click")
assert button.label == "Click"
assert button.variant == "primary"
assert button.disabled is False
def test_button_context():
button = Button(label="Click", variant="danger")
context = button.get_context_data()
assert context["label"] == "Click"
assert context["variant"] == "danger"
Test validation:
import pytest
from brickastley import BrickValidationError
from myapp.bricks import Button
def test_missing_required_kwarg():
with pytest.raises(BrickValidationError):
Button() # Missing required 'label'
Test extra kwargs:
def test_extra_collected():
button = Button(label="Click", class_="mt-4", data_id="123")
assert button.extra == {"class_": "mt-4", "data_id": "123"}
def test_extra_in_context():
button = Button(label="Click", id="my-btn")
context = button.get_context_data()
assert context["extra"]["id"] == "my-btn"