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

user-preferences.mddocs/

User Preferences

User-specific preferences system with models, forms, admin integration, and API support for per-user settings. This enables individual users to have their own preference values separate from global site settings.

Capabilities

User Preference Model

Django model for storing user-specific preferences with foreign key relationship to Django's user model.

class UserPreferenceModel(PerInstancePreferenceModel):
    """
    Model for preferences tied to User instances.
    
    Fields:
    - instance (ForeignKey): Points to AUTH_USER_MODEL
    - section (CharField): Inherited from PerInstancePreferenceModel
    - name (CharField): Inherited from PerInstancePreferenceModel
    - raw_value (TextField): Inherited from PerInstancePreferenceModel
    
    Meta:
    - unique_together = ("instance", "section", "name")
    """
    instance = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='preferences'
    )
    
    class Meta:
        unique_together = ("instance", "section", "name")

User Preference Registry

Registry specifically for managing user-specific preferences with proper instance handling.

class UserPreferenceRegistry(PerInstancePreferenceRegistry):
    """
    Registry for user-specific preferences.
    
    Manages preferences that are tied to individual user accounts,
    allowing each user to have their own preference values.
    """
    name = 'user'
    preference_model = UserPreferenceModel

# Global instance for registering user preferences
user_preferences_registry: UserPreferenceRegistry

User Preference Forms

Django forms for editing user preferences with proper user context and validation.

class UserSinglePreferenceForm(SinglePerInstancePreferenceForm):
    """
    Form for editing a single user preference.
    
    Automatically handles user instance association and
    preference validation within user context.
    """
    class Meta:
        model = UserPreferenceModel
        fields = '__all__'

class UserPreferenceForm(PreferenceForm):
    """
    Form for editing multiple user preferences.
    
    Provides interface for bulk editing of user preferences
    with proper validation and user context.
    """
    registry = user_preferences_registry

def user_preference_form_builder(instance, preferences=None, **kwargs):
    """
    Build dynamic form for user preferences.
    
    Args:
    - instance: User instance
    - preferences: List of specific preferences to include (optional)
    - **kwargs: Additional form options
    
    Returns:
    Dynamic UserPreferenceForm class configured for the user
    """

User Preference Admin

Django admin integration for managing user preferences with user-friendly interface.

class UserPreferenceAdmin(PerInstancePreferenceAdmin):
    """
    Admin interface for user preferences.
    
    Attributes:
    - search_fields: Includes "instance__username" for user search
    - form: UserSinglePreferenceForm
    - list_display: Shows user information along with preference details
    """
    search_fields = PerInstancePreferenceAdmin.search_fields + ("instance__username",)
    form = UserSinglePreferenceForm
    
    def get_queryset(self, request):
        """Filter queryset based on user permissions."""
    
    def has_change_permission(self, request, obj=None):
        """Check if user can change this preference."""
    
    def has_delete_permission(self, request, obj=None):
        """Check if user can delete this preference."""

User Preference API

REST API components for accessing user preferences through API endpoints.

class UserPreferenceSerializer(PreferenceSerializer):
    """
    Serializer for user preferences API.
    
    Includes user context and proper permission handling
    for user-specific preference access.
    """
    
    def validate(self, attrs):
        """Validate user preference data."""
    
    def update(self, instance, validated_data):
        """Update user preference with proper user context."""

class UserPreferencesViewSet(PerInstancePreferenceViewSet):
    """
    API viewset for user preferences.
    
    Endpoints:
    - GET /user-preferences/ - List current user's preferences
    - GET /user-preferences/{identifier}/ - Get specific user preference
    - PUT /user-preferences/{identifier}/ - Update user preference
    - POST /user-preferences/bulk/ - Bulk update user preferences
    """
    queryset = UserPreferenceModel.objects.all()
    serializer_class = UserPreferenceSerializer
    permission_classes = [IsAuthenticated]
    
    def get_related_instance(self):
        """Return current user as the related instance."""
        return self.request.user
    
    def get_queryset(self):
        """Filter preferences to current user only."""
        return super().get_queryset().filter(instance=self.request.user)

Usage Examples

Defining User Preferences

# user_preferences_registry.py (or in your app's dynamic_preferences_registry.py)
from dynamic_preferences.preferences import Section
from dynamic_preferences.users.registries import user_preferences_registry
from dynamic_preferences.types import BooleanPreference, StringPreference, ChoicePreference

# Define sections for user preferences
interface = Section('interface', 'Interface Settings')
notifications = Section('notifications', 'Notification Settings')

@user_preferences_registry.register
class Theme(ChoicePreference):
    section = interface
    name = 'theme'
    default = 'light'
    verbose_name = 'Color Theme'
    choices = (
        ('light', 'Light Theme'),
        ('dark', 'Dark Theme'),
        ('auto', 'Auto (System)'),
    )

@user_preferences_registry.register
class Language(ChoicePreference):
    section = interface
    name = 'language'
    default = 'en'
    verbose_name = 'Language'
    choices = (
        ('en', 'English'),
        ('es', 'Spanish'),
        ('fr', 'French'),
    )

@user_preferences_registry.register
class EmailNotifications(BooleanPreference):
    section = notifications
    name = 'email_enabled'
    default = True
    verbose_name = 'Email Notifications'
    help_text = 'Receive notifications via email'

@user_preferences_registry.register
class NotificationFrequency(ChoicePreference):
    section = notifications
    name = 'frequency'
    default = 'daily'
    verbose_name = 'Notification Frequency'
    choices = (
        ('immediate', 'Immediate'),
        ('daily', 'Daily Digest'),
        ('weekly', 'Weekly Summary'),
        ('never', 'Never'),
    )

Accessing User Preferences in Views

from dynamic_preferences.users.registries import user_preferences_registry

def user_dashboard(request):
    """View that uses user preferences for personalization."""
    if request.user.is_authenticated:
        # Get user preferences manager
        user_preferences = user_preferences_registry.manager(instance=request.user)
        
        # Access user preferences
        theme = user_preferences['interface__theme']
        language = user_preferences['interface__language']
        email_notifications = user_preferences['notifications__email_enabled']
        
        # Update user preference
        if request.method == 'POST':
            if 'theme' in request.POST:
                user_preferences['interface__theme'] = request.POST['theme']
    else:
        # Default preferences for anonymous users
        theme = 'light'
        language = 'en'
        email_notifications = False
    
    return render(request, 'dashboard.html', {
        'theme': theme,
        'language': language,
        'email_notifications': email_notifications,
    })

def user_settings(request):
    """View for user preference management."""
    if not request.user.is_authenticated:
        return redirect('login')
    
    user_preferences = user_preferences_registry.manager(instance=request.user)
    
    if request.method == 'POST':
        # Update multiple preferences
        updates = {}
        if 'theme' in request.POST:
            updates['interface__theme'] = request.POST['theme']
        if 'language' in request.POST:
            updates['interface__language'] = request.POST['language']
        if 'email_notifications' in request.POST:
            updates['notifications__email_enabled'] = request.POST.get('email_notifications') == 'on'
        
        # Bulk update
        for key, value in updates.items():
            user_preferences[key] = value
        
        messages.success(request, 'Settings updated successfully!')
        return redirect('user_settings')
    
    # Get current preferences for form
    current_prefs = {
        'theme': user_preferences['interface__theme'],
        'language': user_preferences['interface__language'],
        'email_notifications': user_preferences['notifications__email_enabled'],
    }
    
    return render(request, 'user_settings.html', {
        'preferences': current_prefs,
    })

Using User Preference Forms

from dynamic_preferences.users.forms import user_preference_form_builder

def user_preferences_form_view(request):
    """View using dynamic user preference form."""
    if not request.user.is_authenticated:
        return redirect('login')
    
    # Build form for current user
    UserPreferenceForm = user_preference_form_builder(
        instance=request.user,
        section='interface'  # Only interface preferences
    )
    
    if request.method == 'POST':
        form = UserPreferenceForm(request.POST)
        if form.is_valid():
            form.update_preferences()
            messages.success(request, 'Preferences updated!')
            return redirect('user_preferences')
    else:
        form = UserPreferenceForm()
    
    return render(request, 'user_preference_form.html', {'form': form})

# Alternative: Form for specific preferences
def notification_settings_view(request):
    """View for notification-specific preferences."""
    if not request.user.is_authenticated:
        return redirect('login')
    
    NotificationForm = user_preference_form_builder(
        instance=request.user,
        preferences=['notifications__email_enabled', 'notifications__frequency']
    )
    
    if request.method == 'POST':
        form = NotificationForm(request.POST)
        if form.is_valid():
            form.update_preferences()
            messages.success(request, 'Notification settings updated!')
            return redirect('notification_settings')
    else:
        form = NotificationForm()
    
    return render(request, 'notification_form.html', {'form': form})

Template Context Processor

# context_processors.py
from dynamic_preferences.users.registries import user_preferences_registry

def user_preferences(request):
    """Add user preferences to template context."""
    if request.user.is_authenticated:
        user_prefs = user_preferences_registry.manager(instance=request.user)
        return {
            'user_preferences': user_prefs,
            'user_theme': user_prefs['interface__theme'],
        }
    return {
        'user_preferences': None,
        'user_theme': 'light',
    }

# settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'OPTIONS': {
            'context_processors': [
                # ... other context processors
                'myapp.context_processors.user_preferences',
            ],
        },
    },
]

API Usage

# JavaScript example for user preference API
async function getUserPreferences() {
    const response = await fetch('/api/user-preferences/', {
        headers: {
            'Authorization': `Bearer ${getAccessToken()}`
        }
    });
    return response.json();
}

async function updateUserPreference(identifier, value) {
    const response = await fetch(`/api/user-preferences/${identifier}/`, {
        method: 'PATCH',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${getAccessToken()}`
        },
        body: JSON.stringify({ value })
    });
    return response.json();
}

// Update theme preference
await updateUserPreference('interface__theme', 'dark');

// Bulk update preferences
async function bulkUpdateUserPreferences(preferences) {
    const response = await fetch('/api/user-preferences/bulk/', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${getAccessToken()}`
        },
        body: JSON.stringify({ preferences })
    });
    return response.json();
}

Custom User Preference Model

from dynamic_preferences.users.models import UserPreferenceModel

class ExtendedUserPreference(UserPreferenceModel):
    """Extended user preference model with additional fields."""
    
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    is_public = models.BooleanField(default=False)
    
    class Meta:
        db_table = 'extended_user_preferences'

# Update registry to use custom model
class ExtendedUserPreferenceRegistry(UserPreferenceRegistry):
    preference_model = ExtendedUserPreference

extended_user_preferences_registry = ExtendedUserPreferenceRegistry()

# Register preferences with extended registry
@extended_user_preferences_registry.register
class PublicProfile(BooleanPreference):
    section = interface
    name = 'public_profile'
    default = False
    verbose_name = 'Public Profile'

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