Dynamic global and instance settings for your django project
—
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.
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")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: UserPreferenceRegistryDjango 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
"""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."""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)# 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'),
)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,
})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})# 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',
],
},
},
]# 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();
}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