Conditional Fields Guide - djadmin-formset¶
Plugin: djadmin-formset
Version: 0.1.0 (Alpha)
Last Updated: October 21, 2025
Overview¶
Conditional fields allow you to show or hide form fields dynamically based on the values of other fields. This is powered by django-formset's client-side reactivity system and requires no page refresh.
Prerequisites¶
djadmin-formsetplugin installed- Layout API with
Fieldcomponents
Basic Usage¶
Show Field When Condition is True¶
Use the show_if attribute to display a field only when a condition matches:
from djadmin import ModelAdmin, register, Layout, Field
@register(Product)
class ProductAdmin(ModelAdmin):
layout = Layout(
Field('name'),
Field('product_type'),
# Show 'weight' only for physical products
Field('weight', show_if=".product_type === 'physical'"),
# Show 'file_size' only for digital products
Field('file_size', show_if=".product_type === 'digital'"),
)
How it works:
- When product_type changes to 'physical', the weight field appears
- When product_type changes to 'digital', the file_size field appears
- Fields are hidden/shown instantly via JavaScript (no page reload)
Hide Field When Condition is True¶
Use the hide_if attribute for inverse logic:
@register(Order)
class OrderAdmin(ModelAdmin):
layout = Layout(
Field('status'),
Field('shipped_date'),
# Hide 'cancel_reason' unless order is cancelled
Field('cancel_reason', hide_if=".status !== 'cancelled'"),
# Equivalent to: show_if=".status === 'cancelled'"
)
Expression Syntax¶
Conditional expressions use django-formset's expression language, which is similar to JavaScript:
Accessing Field Values¶
Use the dot notation to reference field values:
Comparison Operators¶
# Equality
show_if=".type === 'premium'"
# Inequality
show_if=".type !== 'basic'"
# Greater than / Less than
show_if=".quantity > 10"
show_if=".price < 100"
# Greater than or equal / Less than or equal
show_if=".quantity >= 5"
show_if=".price <= 50"
Logical Operators¶
# AND
show_if=".status === 'active' && .verified === true"
# OR
show_if=".type === 'premium' || .type === 'enterprise'"
# NOT
show_if="!(.status === 'deleted')"
Boolean Fields¶
# Show when checkbox is checked
show_if=".is_published === true"
# Show when checkbox is NOT checked
show_if=".is_published === false"
# Or use hide_if
hide_if=".is_published === true"
Null/Undefined Checks¶
# Show only if field has a value
show_if=".discount !== null && .discount !== ''"
# Show only if field is empty
show_if=".discount === null || .discount === ''"
Advanced Examples¶
Example 1: Multi-Level Conditionals¶
@register(ShippingAddress)
class ShippingAddressAdmin(ModelAdmin):
layout = Layout(
Field('country'),
Field('state', show_if=".country === 'US'"),
Field('province', show_if=".country === 'CA'"),
Field('postal_code', show_if=".country === 'US' || .country === 'CA'"),
Field('zip_code', show_if=".country === 'US'"),
)
Example 2: Dependent Cascade¶
@register(Vehicle)
class VehicleAdmin(ModelAdmin):
layout = Layout(
Field('vehicle_type'), # car, motorcycle, bicycle
# Level 1: Show based on vehicle type
Field('fuel_type', show_if=".vehicle_type === 'car' || .vehicle_type === 'motorcycle'"),
# Level 2: Show based on fuel type
Field('battery_capacity', show_if=".fuel_type === 'electric'"),
Field('fuel_tank_size', show_if=".fuel_type === 'gasoline' || .fuel_type === 'diesel'"),
# Always show for certain types
Field('num_wheels', show_if=".vehicle_type === 'car'"),
Field('frame_size', show_if=".vehicle_type === 'bicycle'"),
)
Example 3: Numeric Ranges¶
@register(Discount)
class DiscountAdmin(ModelAdmin):
layout = Layout(
Field('discount_type'), # percentage, fixed
Field('percentage', show_if=".discount_type === 'percentage'"),
Field('fixed_amount', show_if=".discount_type === 'fixed'"),
# Show warning for high discounts
Field('approval_required', show_if=".percentage > 50 || .fixed_amount > 100"),
)
Example 4: Complex Business Logic¶
@register(Subscription)
class SubscriptionAdmin(ModelAdmin):
layout = Layout(
Field('plan'), # basic, premium, enterprise
Field('is_trial'),
# Show billing fields only for non-trial paid plans
Field('billing_cycle',
show_if="(.plan === 'premium' || .plan === 'enterprise') && .is_trial === false"
),
# Show trial end date only during trial
Field('trial_end_date', show_if=".is_trial === true"),
# Show upgrade offer only for basic plan
Field('upgrade_offer', show_if=".plan === 'basic' && .is_trial === false"),
)
Example 5: Conditional Fieldsets¶
You can use conditional fields inside fieldsets:
from djadmin import ModelAdmin, register, Layout, Field, Fieldset
@register(Event)
class EventAdmin(ModelAdmin):
layout = Layout(
Fieldset('Basic Information',
Field('title'),
Field('event_type'), # 'online', 'in-person', 'hybrid'
),
# Fieldset with conditional fields
Fieldset('Location Details',
Field('venue_name', show_if=".event_type === 'in-person' || .event_type === 'hybrid'"),
Field('address', show_if=".event_type === 'in-person' || .event_type === 'hybrid'"),
Field('zoom_link', show_if=".event_type === 'online' || .event_type === 'hybrid'"),
),
)
Combining with Rows¶
Conditional fields work inside Row layouts:
from djadmin import ModelAdmin, register, Layout, Field, Row
@register(Product)
class ProductAdmin(ModelAdmin):
layout = Layout(
Field('product_type'),
# Conditional row - both fields show/hide together
Row(
Field('length', css_classes=['flex-1'], show_if=".product_type === 'physical'"),
Field('width', css_classes=['flex-1'], show_if=".product_type === 'physical'"),
Field('height', css_classes=['flex-1'], show_if=".product_type === 'physical'"),
),
)
Validation Considerations¶
Required Fields¶
IMPORTANT: If a field is marked as required=True but is hidden via show_if, django-formset handles this intelligently:
- When visible: Field is required (validation enforced)
- When hidden: Field is NOT required (validation skipped)
This is automatic - no special handling needed!
Example: Conditional Required Fields¶
@register(CustomerOrder)
class CustomerOrderAdmin(ModelAdmin):
layout = Layout(
Field('requires_shipping'), # Boolean
# Required only when shipping is needed
Field('shipping_address',
required=True,
show_if=".requires_shipping === true"
),
Field('shipping_method',
required=True,
show_if=".requires_shipping === true"
),
# Always required
Field('billing_address', required=True),
)
Performance Considerations¶
Client-Side Execution¶
Conditional logic runs entirely in the browser via JavaScript:
- ✅ No server roundtrips
- ✅ Instant feedback
- ✅ Works offline (after page load)
- ✅ No network latency
Limitations¶
- ❌ Cannot access database values
- ❌ Cannot call Django/Python functions
- ❌ Limited to values in the current form
For server-side logic, use Django's form clean() methods:
# For complex validation requiring database access
class MyForm(forms.ModelForm):
def clean(self):
data = super().clean()
if data['type'] == 'premium':
# Server-side check
if not self.instance.user.has_premium_access():
raise ValidationError('User not authorized for premium')
return data
Troubleshooting¶
Conditions Not Working¶
Symptom: Fields don't show/hide when values change
Checklist:
1. ✅ Plugin installed? Check INSTALLED_APPS includes 'djadmin_formset'
2. ✅ JavaScript loaded? Check browser console for errors
3. ✅ Syntax correct? Use === not =, && not and
4. ✅ Field name correct? Use exact field name (case-sensitive)
Debug:
// Browser console
// Check if formset is initialized
document.querySelector('form[data-formset]')
// Check field attributes
document.querySelector('[name="field_name"]').getAttribute('df-show')
Wrong Field Reference¶
Symptom: Condition always evaluates to false
Common mistake:
# WRONG - missing dot prefix
show_if="status === 'active'"
# CORRECT - dot prefix required
show_if=".status === 'active'"
Boolean Field Issues¶
Symptom: Boolean conditions not working
Solution: Use === true or === false:
Quotes in String Values¶
Symptom: Syntax error in expressions with quotes
Solution: Use consistent quotes:
# WRONG - mixing quotes
show_if='.status === "active"'
# CORRECT - use single quotes for expression, escape inner quotes
show_if=".status === 'active'"
# CORRECT - or double quotes for expression, escape inner
show_if=".status === \"active\""
Best Practices¶
1. Keep Expressions Simple¶
# Good - simple, readable
show_if=".type === 'premium'"
# Avoid - complex, hard to maintain
show_if="(.type === 'premium' && .verified === true && .credits > 100) || (.type === 'enterprise' && .trial === false)"
For complex logic, consider splitting into multiple fields or using computed fields.
2. Use Meaningful Field Names¶
# Good - clear what it controls
Field('requires_shipping')
Field('shipping_address', show_if=".requires_shipping === true")
# Avoid - unclear
Field('flag1')
Field('address', show_if=".flag1 === true")
3. Document Complex Conditions¶
# Document the business logic
Field('approval_needed',
# Show approval field when:
# - Discount > 50% OR
# - Fixed discount > $100
show_if=".percentage > 50 || .fixed_amount > 100"
)
4. Test All Branches¶
Test that fields show/hide correctly for all possible values:
def test_conditional_fields(admin_client, product_factory):
"""Test conditional fields show/hide correctly."""
# Test physical product - weight visible
response = admin_client.get(...)
# Check weight field has df-show attribute
# Test digital product - file_size visible
# ...
5. Provide Visual Feedback¶
When fields appear/disappear, ensure smooth transitions:
Expression Reference¶
Operators¶
| Operator | Example | Description |
|---|---|---|
=== |
.status === 'active' |
Strict equality |
!== |
.status !== 'deleted' |
Strict inequality |
> |
.quantity > 10 |
Greater than |
< |
.price < 100 |
Less than |
>= |
.quantity >= 5 |
Greater than or equal |
<= |
.price <= 50 |
Less than or equal |
&& |
.a === 1 && .b === 2 |
Logical AND |
|| |
.a === 1 || .b === 2 |
Logical OR |
! |
!(.status === 'deleted') |
Logical NOT |
Data Types¶
| Type | Example Values | Usage |
|---|---|---|
| String | 'active', 'pending' |
Use single quotes |
| Number | 10, 99.99 |
No quotes |
| Boolean | true, false |
Lowercase, no quotes |
| Null | null |
Lowercase, no quotes |
See Also¶
- Computed Fields Guide - Auto-calculate field values
- Inline Editing Guide - Collections with conditionals
- Layout Integration Guide - Overall plugin architecture
- django-formset Expressions