name: django-glue-forms description: Django Glue Alpine.js integration for reactive Django forms
Django Glue Forms
Overview
Django Glue Forms provides seamless Alpine.js integration for Django forms, enabling reactive, two-way data binding between server-side Django forms and client-side JavaScript. It bridges the gap between Django's server-side validation and Alpine.js's reactive UI capabilities.
Key Features
- ModelObjectGlue: Bind Django model instances or form data to Alpine.js reactive objects
- Automatic Field Generation: Model fields auto-converted to Alpine.js compatible formats
- Session Management: Persistent state across requests with keep-alive functionality
- Permission Control: View, change, and delete access levels
- Form Field Templates: 20+ pre-built field templates for common Django field types
Core Concepts
Two Form Approaches
- ModelObjectGlue - When your form is backed by a model
- Frontend Field Initialization - Collecting data that is not related to a Django Model
Note: Both approaches can be used together in the same form.
Access Levels
from django_glue.access.access import Access
# Permission hierarchy (lowest to highest)
Access.VIEW # Read-only access
Access.CHANGE # Read and modify access
Access.DELETE # Full access including delete
Session Storage
All glue objects are stored in Django session with:
- Unique encoded key (includes request path for isolation)
- Automatic cleanup via middleware
- Keep-alive mechanism to prevent expiration during active use
Field Templates Available
Located in django_glue/templates/django_glue/form/field/:
| Template | Use Case | Django Field Type |
|---|---|---|
char_field.html |
Text inputs | CharField |
email_field.html |
Email inputs | EmailField |
password_field.html |
Password inputs | CharField (password) |
number_field.html |
Number inputs | IntegerField, FloatField |
date_field.html |
Date inputs | DateField |
datetime_field.html |
Datetime inputs | DateTimeField |
time_field.html |
Time inputs | TimeField |
text_field.html |
Textarea inputs | TextField |
select_field.html |
Single select dropdowns | ChoiceField, ForeignKey |
multi_select_field.html |
Multi-select dropdowns | ManyToManyField |
single_checkbox_field.html |
Boolean checkboxes | BooleanField |
radio_field.html |
Radio buttons | ChoiceField |
search_and_select_field.html |
Searchable selects | ForeignKey |
color_field.html |
Color pickers | ColorField |
telephone_field.html |
Phone number inputs | CharField (phone) |
range_field.html |
Range sliders | FloatField (range) |
decimal_field.html |
Decimal inputs | DecimalField |
_base_file_field.html |
File upload base | FileField |
single_file_field.html |
Single file upload | FileField |
multi_file_field.html |
Multiple file upload | FileField (multiple) |
_multi_checkbox_field.html |
Multiple checkboxes | MultipleChoiceField |
All field templates extend base_field.html which handles:
- Field initialization from session
- Attribute binding (required, disabled, etc.)
- Error display
- Help text tooltips
How It Works
1. Server Initialization
dg.glue_model_object(request, 'company', company_instance, 'change')
What happens:
- Creates
ModelObjectGlueobject with model metadata - Serializes field definitions (name, type, label, choices, validation rules)
- Stores in Django session under encoded unique name
- Sets "keep-alive" flag to maintain session
2. Client Initialization
company: new ModelObjectGlue('company')
What happens:
- Alpine.js component loads
init()callsthis.company.get()- Makes AJAX request to fetch field data from session
- Populates reactive object with field definitions and values
- Fields become available for binding
3. Field Binding
{% include 'django_glue/form/field/char_field.html' with glue_model_field='company.name' %}
What happens:
- Base template (
base_field.html) sets up Alpine.js data binding x-model="value"binds tocompany.name.value- Field attributes (required, disabled, etc.) are applied dynamically
- Validation errors display automatically
- Changes sync between Alpine.js and session
4. Form Submission
<form method="post">
What happens:
- Standard Django form submission
- Alpine.js values sent as POST data
- Django form validates against
cleaned_data - Server-side validation errors displayed via
show_form_errors() - On success, redirect or update model
Real-World Examples
Your Codebase: Company Form (ModelForm)
View (app/company/views/form_views.py):
def _form_view(request: WSGIRequest, pk: int):
company = get_object_or_null_obj(models.Company, pk=pk)
dg.glue_model_object(request, 'company', company, 'view')
if request.method == 'POST':
form = forms.CompanyForm(request.POST, instance=company)
if form.is_valid():
company, _ = company.services.save_model_obj(**form.cleaned_data)
add_form_activity(company, pk, request.user)
return redirect(return_url)
show_form_errors(request, form)
else:
form = forms.CompanyForm(instance=company)
return portal_views.form_view(
request,
form=form,
obj=company,
template='company/page/form_page.html',
)
Template (templates/company/form/form.html):
<form method="post" x-data="{
async init() {
await this.company.get()
},
company: new ModelObjectGlue('company')
}">
{% csrf_token %}
{% include 'django_glue/form/field/char_field.html' with glue_model_field='company.name' %}
{% include 'django_glue/form/field/text_field.html' with glue_model_field='company.description' %}
{% include 'django_spire/contrib/form/button/form_submit_button.html' %}
</form>
Frontend Field Initialization
There are two ways to bind form fields with Django Glue:
- ModelObjectGlue - Bind entire model objects (all fields at once)
- Manual Field Initialization - Initialize individual fields directly in Alpine.js
Both approaches use the same field templates. The difference is how you initialize the data in x-data.
Available Glue Field Types
| Field Type | Use Case |
|---|---|
GlueCharField |
Text inputs |
GlueIntegerField |
Integer inputs with min/max/step |
GlueDecimalField |
Decimal inputs with step precision |
GlueBooleanField |
Boolean checkboxes (Yes/No choices) |
GlueDateField |
Date inputs with min/max |
Common Field Properties
All Glue fields support these properties:
| Property | Type | Description |
|---|---|---|
value |
any | Current field value |
label |
string | Field label text |
choices |
array | Array of [value, label] pairs |
required |
boolean | Make field required |
hidden |
boolean | Hide field from view |
read_only |
boolean | Make field read-only |
disabled |
boolean | Disable field input |
autofocus |
boolean | Auto-focus on field |
help_text |
string | Help text tooltip |
set_attribute(name, value) |
method | Set custom HTML attribute |
remove_attribute(name) |
method | Remove HTML attribute |
hide_label() |
method | Hide label visually |
show_label() |
method | Show label |
Manual Field Initialization
Initialize any field type directly in Alpine.js x-data:
<form method="post" x-data="{
async init() {
// Set initial value
this.partner_field.value = {{ partner.pk|default_if_none:'null' }}
// Load choices dynamically
this.partner_field.choices = await this.partners.to_choices()
// Modify field properties
this.partner_field.label = 'Partner'
this.partner_field.required = true
// Watch for changes
this.$watch('partner_field.value', async () => {
this.agreement_field.choices = await this.get_agreements.call({
'partner_id': this.partner_field.value
})
})
},
// Initialize standalone fields
partner_field: new GlueCharField('partner'),
agreement_field: new GlueCharField('agreement'),
// Supporting objects
partners: new QuerySetGlue('partners'),
get_agreements: new FunctionGlue('get_agreements'),
}">
{% csrf_token %}
<!-- Use glue_field parameter for standalone fields -->
{% include 'django_glue/form/field/search_and_select_field.html' with glue_field='partner_field' %}
{% include 'django_glue/form/field/search_and_select_field.html' with glue_field='agreement_field' %}
<button class="btn btn-app-primary">Submit</button>
</form>
Mixing Both Approaches
You can use ModelObjectGlue and standalone fields in the same form:
<form method="post" x-data="{
async init() {
// Load model object
await this.scope_of_work.get()
// Initialize standalone fields
this.partner_field.choices = await this.partners.to_choices()
},
// Model-backed fields
scope_of_work: new ModelObjectGlue('scope_of_work'),
// Standalone fields
partner_field: new GlueCharField('partner'),
// Supporting objects
partners: new QuerySetGlue('partners'),
}">
{% csrf_token %}
<!-- Model field (uses glue_model_field) -->
{% include 'django_glue/form/field/char_field.html' with glue_model_field='scope_of_work.name' %}
<!-- Standalone field (uses glue_field) -->
{% include 'django_glue/form/field/search_and_select_field.html' with glue_field='partner_field' %}
<button class="btn btn-app-primary">Submit</button>
</form>
Key Points:
- Model fields use
glue_model_field='object.field' - Standalone fields use
glue_field='field_name' - Both use the same field templates
- Initialize standalone fields in
x-datawithnew Glue*Field('name')
Advanced Features
Field Inclusion/Exclusion
# Only include specific fields
dg.glue_model_object(request, 'obj', instance, fields=('name', 'email'))
# Exclude specific fields
dg.glue_model_object(request, 'obj', instance, exclude=('password', 'secret'))
# Include all except some
dg.glue_model_object(request, 'obj', instance,
fields=('__all__',),
exclude=('internal_notes',))
Exposing Model Methods
def my_view(request):
obj = MyModel.objects.get(pk=1)
dg.glue_model_object(request, 'obj', obj, methods=('get_full_name',))
# In template
<div x-text="company.get_full_name()"></div>
Exclude Sensitive Fields
Always exclude sensitive data:
dg.glue_model_object(
request,
'user',
user_instance,
exclude=('password', 'secret_key', 'internal_notes')
)
Code Review Checklist
When reviewing Django Glue Forms code, verify the following:
- Glue Initialization: All views using Glue call
dg.glue_model_object()before rendering - Name Consistency: The
unique_nameparameter matches between server and client code - Async Initialization: Alpine.js components use
async init()with.get()call - CSRF Token: All forms include
{% csrf_token %} - Field Parameter: Using
glue_model_fieldfor all ModelObjectGlue fields - Access Level: Appropriate access level ('view', 'change', 'delete') is set
- Sensitive Fields: Password and secret fields are excluded from glue objects
- Field Templates: Appropriate field template is used for each field type
- Empty Instance for New Objects: Regular forms initialize Glue with empty model instance