CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-django-dynamic-preferences

Dynamic global and instance settings for your django project

Pending
Overview
Eval results
Files

forms-views.mddocs/

Forms and Views

Django forms and views for web-based preference editing with dynamic form generation and validation. This provides comprehensive web interface components for managing preferences through standard Django forms and views.

Capabilities

Single Preference Forms

Forms for editing individual preferences with automatic field generation based on preference type.

class AbstractSinglePreferenceForm(forms.ModelForm):
    """
    Base form for editing single preferences.
    
    Automatically generates appropriate form field based on
    preference type and handles validation and saving.
    """
    
    def __init__(self, *args, **kwargs):
        """
        Initialize form with preference-specific field.
        
        Args:
        - *args: Standard form args
        - **kwargs: Standard form kwargs plus preference info
        """
    
    def clean(self):
        """
        Validate preference exists in registry and value is valid.
        
        Returns:
        Cleaned form data
        
        Raises:
        - ValidationError: If preference not found or value invalid
        """
    
    def save(self, commit=True):
        """
        Save preference value through preference system.
        
        Args:
        - commit: Whether to save to database immediately
        
        Returns:
        Preference model instance
        """

class SinglePerInstancePreferenceForm(AbstractSinglePreferenceForm):
    """
    Form for editing single per-instance preferences.
    
    Handles preferences tied to specific model instances,
    such as user-specific preferences.
    """
    class Meta:
        model = None  # Set by subclasses
        fields = '__all__'

class GlobalSinglePreferenceForm(AbstractSinglePreferenceForm):
    """
    Form for editing single global preferences.
    
    Specialized for global (site-wide) preferences with
    appropriate model and field configuration.
    """
    class Meta:
        model = GlobalPreferenceModel
        fields = '__all__'

Multiple Preference Forms

Forms for editing multiple preferences simultaneously with section organization and bulk updates.

class PreferenceForm(forms.Form):
    """
    Base form for editing multiple preferences.
    
    Dynamically generates fields for multiple preferences
    and handles bulk updates through preference managers.
    
    Attributes:
    - registry: Associated preference registry
    """
    registry = None
    
    def __init__(self, *args, **kwargs):
        """
        Initialize form with preference fields.
        
        Args:
        - *args: Standard form args
        - **kwargs: Standard form kwargs plus preferences/section info
        """
    
    def update_preferences(self, **kwargs):
        """
        Update multiple preferences from form data.
        
        Args:
        - **kwargs: Additional options for preference manager
        
        Returns:
        Dictionary of updated preferences
        """

class GlobalPreferenceForm(PreferenceForm):
    """
    Form for editing multiple global preferences.
    
    Provides interface for bulk editing of global preferences
    with proper validation and section organization.
    """
    registry = global_preferences_registry

Dynamic Form Builders

Factory functions for creating dynamic preference forms based on registry configuration and filtering options.

def preference_form_builder(form_base_class, preferences=None, **kwargs):
    """
    Build dynamic forms for preferences.
    
    Creates form classes dynamically based on registry configuration
    and filtering criteria. Supports section filtering, instance binding,
    and custom field configuration.
    
    Args:
    - form_base_class: Base form class with registry attribute
    - preferences (list): Specific preferences to include (optional)
    - **kwargs: Additional options:
        - section (str): Filter by section
        - instance: Instance for per-instance preferences
        - exclude (list): Preferences to exclude
        - field_kwargs (dict): Custom field arguments
    
    Returns:
    Dynamically created form class
    """

def global_preference_form_builder(preferences=None, **kwargs):
    """
    Shortcut for building global preference forms.
    
    Args:
    - preferences (list): Specific preferences to include (optional)
    - **kwargs: Additional options (section, exclude, etc.)
    
    Returns:
    Dynamic GlobalPreferenceForm class
    """

Preference Views

Django views for displaying and editing preferences with proper permissions and form handling.

class RegularTemplateView(TemplateView):
    """
    Simple template view for testing preference context.
    
    Template: "dynamic_preferences/testcontext.html"
    """
    template_name = "dynamic_preferences/testcontext.html"

class PreferenceFormView(FormView):
    """
    Display form for updating preferences by section.
    
    Provides complete view for preference editing with
    section filtering, form generation, and success handling.
    
    Attributes:
    - registry: Registry for preference lookups
    - form_class: Form class for preference updates
    - template_name: "dynamic_preferences/form.html"
    """
    registry = None
    form_class = None
    template_name = "dynamic_preferences/form.html"
    success_url = None
    
    def dispatch(self, request, *args, **kwargs):
        """
        Setup section from URL arguments.
        
        Args:
        - request: HTTP request
        - *args: URL positional arguments
        - **kwargs: URL keyword arguments (including section)
        
        Returns:
        HTTP response
        """
    
    def get_form_class(self):
        """
        Build dynamic form class based on section and registry.
        
        Returns:
        Form class configured for current section/preferences
        """
    
    def get_context_data(self, **kwargs):
        """
        Add registry and section information to template context.
        
        Args:
        - **kwargs: Base context data
        
        Returns:
        Updated context dictionary
        """
    
    def form_valid(self, form):
        """
        Update preferences on successful form submission.
        
        Args:
        - form: Valid form instance
        
        Returns:
        HTTP response (redirect to success URL)
        """

Usage Examples

Basic Preference Form View

from dynamic_preferences.views import PreferenceFormView
from dynamic_preferences.registries import global_preferences_registry

class GlobalPreferenceView(PreferenceFormView):
    """View for editing global preferences."""
    registry = global_preferences_registry
    template_name = 'admin/preferences/global_form.html'
    success_url = '/preferences/global/'
    
    def dispatch(self, request, *args, **kwargs):
        # Check permissions
        if not request.user.is_staff:
            return HttpResponseForbidden()
        return super().dispatch(request, *args, **kwargs)

# URL configuration
urlpatterns = [
    path('preferences/global/', GlobalPreferenceView.as_view(), name='global_preferences'),
    path('preferences/global/<str:section>/', GlobalPreferenceView.as_view(), name='global_preferences_section'),
]

Custom Form with Validation

from dynamic_preferences.forms import global_preference_form_builder
from django.core.exceptions import ValidationError

def custom_preference_view(request):
    """Custom view with custom form validation."""
    
    # Build form for specific section
    PreferenceForm = global_preference_form_builder(section='general')
    
    if request.method == 'POST':
        form = PreferenceForm(request.POST)
        if form.is_valid():
            # Custom validation
            if form.cleaned_data.get('general__maintenance_mode') and not request.user.is_superuser:
                form.add_error(None, 'Only superusers can enable maintenance mode')
            else:
                form.update_preferences()
                messages.success(request, 'Preferences updated successfully!')
                return redirect('preferences')
    else:
        form = PreferenceForm()
    
    return render(request, 'preferences_form.html', {'form': form})

# Advanced form with custom field configuration
def advanced_preference_view(request):
    """View with custom field configuration."""
    
    PreferenceForm = global_preference_form_builder(
        preferences=['general__title', 'general__description'],
        field_kwargs={
            'general__title': {
                'widget': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Enter site title'})
            },
            'general__description': {
                'widget': forms.Textarea(attrs={'class': 'form-control', 'rows': 4})
            }
        }
    )
    
    if request.method == 'POST':
        form = PreferenceForm(request.POST)
        if form.is_valid():
            form.update_preferences()
            return JsonResponse({'success': True})
        else:
            return JsonResponse({'success': False, 'errors': form.errors})
    else:
        form = PreferenceForm()
    
    return render(request, 'advanced_preferences.html', {'form': form})

Section-Based Preference Views

class SectionPreferenceView(PreferenceFormView):
    """View for editing preferences by section."""
    registry = global_preferences_registry
    template_name = 'preferences/section_form.html'
    
    def get_success_url(self):
        section = self.kwargs.get('section')
        return reverse('section_preferences', kwargs={'section': section})
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        section = self.kwargs.get('section')
        
        # Add section information
        context['section'] = section
        context['section_preferences'] = self.registry.preferences(section=section)
        
        # Add navigation for other sections
        context['all_sections'] = self.registry.sections()
        
        return context

# Multiple section views
class PreferencesIndexView(TemplateView):
    """Index view showing all preference sections."""
    template_name = 'preferences/index.html'
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['sections'] = global_preferences_registry.sections()
        return context

# URL patterns
urlpatterns = [
    path('preferences/', PreferencesIndexView.as_view(), name='preferences_index'),
    path('preferences/<str:section>/', SectionPreferenceView.as_view(), name='section_preferences'),
]

AJAX Preference Updates

from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
import json

@method_decorator(csrf_exempt, name='dispatch')
class AjaxPreferenceUpdateView(View):
    """AJAX view for updating individual preferences."""
    
    def post(self, request):
        try:
            data = json.loads(request.body)
            preference_key = data.get('preference')
            value = data.get('value')
            
            if not preference_key or value is None:
                return JsonResponse({'error': 'Missing preference or value'}, status=400)
            
            # Update preference
            global_preferences = global_preferences_registry.manager()
            global_preferences[preference_key] = value
            
            return JsonResponse({
                'success': True,
                'preference': preference_key,
                'value': value,
                'message': 'Preference updated successfully'
            })
            
        except Exception as e:
            return JsonResponse({'error': str(e)}, status=500)

# JavaScript for AJAX updates
"""
async function updatePreference(key, value) {
    const response = await fetch('/preferences/ajax-update/', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'X-CSRFToken': getCsrfToken()
        },
        body: JSON.stringify({
            preference: key,
            value: value
        })
    });
    
    const result = await response.json();
    if (result.success) {
        showMessage('Preference updated!', 'success');
    } else {
        showMessage('Error: ' + result.error, 'error');
    }
}

// Update preference on change
document.addEventListener('change', function(e) {
    if (e.target.classList.contains('preference-field')) {
        const key = e.target.dataset.preference;
        const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
        updatePreference(key, value);
    }
});
"""

Form Widgets and Customization

from django import forms
from dynamic_preferences.forms import global_preference_form_builder

def custom_widget_view(request):
    """View with custom widgets for different preference types."""
    
    # Custom widget configuration
    widget_config = {
        'ui__theme': forms.Select(attrs={'class': 'form-select'}),
        'ui__primary_color': forms.TextInput(attrs={'type': 'color', 'class': 'form-control'}),
        'general__description': forms.Textarea(attrs={'class': 'form-control', 'rows': 5}),
        'general__logo': forms.FileInput(attrs={'class': 'form-control', 'accept': 'image/*'}),
    }
    
    PreferenceForm = global_preference_form_builder(
        section='ui',
        field_kwargs={
            key: {'widget': widget} for key, widget in widget_config.items()
        }
    )
    
    if request.method == 'POST':
        form = PreferenceForm(request.POST, request.FILES)
        if form.is_valid():
            form.update_preferences()
            return redirect('ui_preferences')
    else:
        form = PreferenceForm()
    
    return render(request, 'ui_preferences.html', {'form': form})

# Custom form class with additional methods
class ExtendedPreferenceForm(forms.Form):
    """Extended preference form with additional functionality."""
    registry = global_preferences_registry
    
    def __init__(self, *args, **kwargs):
        self.section = kwargs.pop('section', None)
        super().__init__(*args, **kwargs)
        self.setup_preference_fields()
    
    def setup_preference_fields(self):
        """Setup fields based on preferences."""
        preferences = self.registry.preferences(section=self.section)
        for pref in preferences:
            field = pref.setup_field()
            self.fields[pref.identifier()] = field
    
    def clean(self):
        """Custom validation across preferences."""
        cleaned_data = super().clean()
        
        # Example: Validate theme and color compatibility
        theme = cleaned_data.get('ui__theme')
        primary_color = cleaned_data.get('ui__primary_color')
        
        if theme == 'dark' and primary_color and primary_color.startswith('#F'):
            self.add_error('ui__primary_color', 'Light colors not recommended for dark theme')
        
        return cleaned_data
    
    def save(self):
        """Save all preferences."""
        manager = self.registry.manager()
        for key, value in self.cleaned_data.items():
            manager[key] = value
        return manager

Template Usage

<!-- preferences/section_form.html -->
<form method="post">
    {% csrf_token %}
    
    <h2>{{ section|title }} Preferences</h2>
    
    {% for field in form %}
        <div class="form-group">
            <label for="{{ field.id_for_label }}">{{ field.label }}</label>
            {{ field }}
            {% if field.help_text %}
                <small class="form-text text-muted">{{ field.help_text }}</small>
            {% endif %}
            {% if field.errors %}
                <div class="text-danger">
                    {% for error in field.errors %}
                        <small>{{ error }}</small>
                    {% endfor %}
                </div>
            {% endif %}
        </div>
    {% endfor %}
    
    <button type="submit" class="btn btn-primary">Save Preferences</button>
</form>

<!-- Section navigation -->
<nav class="mt-4">
    <h4>Other Sections</h4>
    <ul class="list-unstyled">
        {% for section in all_sections %}
            <li><a href="{% url 'section_preferences' section.name %}">{{ section.verbose_name|default:section.name }}</a></li>
        {% endfor %}
    </ul>
</nav>

Install with Tessl CLI

npx tessl i tessl/pypi-django-dynamic-preferences

docs

admin-integration.md

core-models.md

django-integration.md

forms-views.md

index.md

preference-types.md

registries.md

rest-api.md

serialization.md

signals.md

user-preferences.md

tile.json