Hooks¶
TemplateHook¶
Adding a hook-point in main_app
‘s template:
# my_main_app/templates/_base.html
{% load hooks_tags %}
<!DOCTYPE html>
<html>
<head>
#...
{% hook 'within_head' %}
#...
</head>
</html>
Tip
Here we are adding a hook-point called within_head
where third-party
apps will be able to insert their code.
Creating a hook listener in a third_party_app
:
# third_party_app/template_hooks.py
from django.template.loader import render_to_string
from django.utils.html import mark_safe, format_html
# Example 1
def css_resources(context, *args, **kwargs):
return mark_safe(u'<link rel="stylesheet" href="%s/app_hook/styles.css">' % settings.STATIC_URL)
# Example 2
def user_about_info(context, *args, **kwargs):
user = context['request'].user
return format_html(
"<b>{name}</b> {last_name}: {about}",
name=user.first_name,
last_name=user.last_name,
about=mark_safe(user.profile.about_html_field) # Some safe (sanitized) html data.
)
# Example 3
def a_more_complex_hook(context, *args, **kwargs):
# If you are doing this a lot, make sure to keep your templates in memory (google: django.template.loaders.cached.Loader)
return render_to_string(
template_name='templates/app_hook/head_resources.html',
context_instance=context
)
# Example 4
def an_even_more_complex_hook(context, *args, **kwargs):
articles = Article.objects.all()
return render_to_string(
template_name='templates/app_hook/my_articles.html',
dictionary={'articles': articles, },
context_instance=context
)
Registering a hook listener in a third_party_app
:
# third_party_app/apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = 'myapp'
verbose_name = 'My App'
def ready(self):
from hooks.templatehook import hook
from third_party_app.template_hooks import css_resources
hook.register("within_head", css_resources)
FormHook¶
Creating a hook-point:
# main_app/formhooks.py
from hooks.formhook import Hook
MyFormHook = Hook()
UserFormHook = Hook(providing_args=['user'])
Adding a hook-point to the main app view:
# main_app/views.py
from main_app import formhooks
# Example 1
def my_view(request):
if request.method == 'POST':
form_hook = formhooks.MyFormHook(data=request.POST)
if form_hook.is_valid():
form_hook.save()
redirect('/')
else:
form_hook = formhooks.MyFormHook()
return response('my_view.html', {'form_hook': form_hook})
# Example 2
def user_profile_update(request):
if request.method == 'POST':
user_form = UserForm(data=request.POST, instance=request.user)
# Hook listeners will receive the user and populate the
# initial data (or instance if a ModelForm is used) accordingly,
# or maybe even query the data base.
user_form_hook = formhooks.UserFormHook(user=request.user, data=request.POST)
if all([user_form.is_valid(), user_form_hook.is_valid()]): # Avoid short-circuit
new_user = user_form.save()
user_form_hook.save(new_user=new_user) # They may receive extra parameter when saving
redirect('/')
else:
user_form = MyForm(instance=request.user)
user_form_hook = formhooks.UserFormHook(user=request.user)
return response('user_profile_update.html', {'user_form': user_form, 'user_form_hook': user_form_hook})
Displaying the forms:
# main_app/templates/my_view.html
{% extends "main_app/_base.html" %}
{% block title %}My forms{% endblock %}
{% block content %}
<h1 class="headline">My forms</h1>
<form action="." method="post">
{% csrf_token %}
{% for f in form_hook %}
{{ f }}
{% endfor %}
<input type="submit" value="Save" />
</form>
{% endblock %}
Creating a hook-listener in a third-party app:
Tip
Hooks listeners are just regular django forms or model forms
# third_party_app/forms.py
from django import forms
from third_party_app.models import MyUserExtension
# Example 1
class MyRegularForm(forms.Form):
""""""
# ...
# Example 2
class MyUserExtensionForm(forms.ModelForm):
class Meta:
model = MyUserExtension
fields = ("gender", "age", "about")
def __init__(user=None, *args, **kwargs):
try:
instance = MyUserExtension.objects.get(user=user)
except MyUserExtension.DoesNotExist:
instance = None
kwargs['instance'] = instance
super(MyUserExtensionForm, self).__init__(*args, **kwargs)
def save(new_user, *args, **kwargs):
self.instance.user = new_user
super(MyUserExtensionForm, self).save(*args, **kwargs)
Registering a hook-listener:
# third_party_app/apps.py
from django.apps import AppConfig
# Example
class MyAppConfig(AppConfig):
name = 'myapp'
verbose_name = 'My App'
def ready(self):
from main_app.formhooks import MyFormHook, UserFormHook
from third_party_app.forms import MyRegularForm, MyUserExtensionForm
MyFormHook.register(MyRegularForm)
UserFormHook.register(MyUserExtensionForm)
SignalHook¶
Tip
Best practices:
- Always document the signals the app will send, include the parameters the receiver should handle.
- Send signals from views, only.
- Avoid sending signals from plugins.
- Try to avoid signal-hell in general. It’s better to be explicit and call the functions that would’ve handle the signal otherwise. Of course, this won’t be possible when there are plugins involved.
Connecting a hook-listener:
# third_party_app/urls.py
from third_party_app.viewhooks import myhandler
from hooks import signalhook
signalhook.hook.connect("my-signal", myhandler)
Sending a signal:
# send from anywhere, app-hook, main-app... view, model, form...
from hooks import signalhook
responses = signalhook.hook.send("my-signal", arg_one="hello", arg_two="world")
responses = signalhook.hook.send("another-signal")
Tip
SignalHook
uses django signals under the hood, so you can do pretty much the same things.