0
# User Preferences
1
2
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.
3
4
## Capabilities
5
6
### User Preference Model
7
8
Django model for storing user-specific preferences with foreign key relationship to Django's user model.
9
10
```python { .api }
11
class UserPreferenceModel(PerInstancePreferenceModel):
12
"""
13
Model for preferences tied to User instances.
14
15
Fields:
16
- instance (ForeignKey): Points to AUTH_USER_MODEL
17
- section (CharField): Inherited from PerInstancePreferenceModel
18
- name (CharField): Inherited from PerInstancePreferenceModel
19
- raw_value (TextField): Inherited from PerInstancePreferenceModel
20
21
Meta:
22
- unique_together = ("instance", "section", "name")
23
"""
24
instance = models.ForeignKey(
25
settings.AUTH_USER_MODEL,
26
on_delete=models.CASCADE,
27
related_name='preferences'
28
)
29
30
class Meta:
31
unique_together = ("instance", "section", "name")
32
```
33
34
### User Preference Registry
35
36
Registry specifically for managing user-specific preferences with proper instance handling.
37
38
```python { .api }
39
class UserPreferenceRegistry(PerInstancePreferenceRegistry):
40
"""
41
Registry for user-specific preferences.
42
43
Manages preferences that are tied to individual user accounts,
44
allowing each user to have their own preference values.
45
"""
46
name = 'user'
47
preference_model = UserPreferenceModel
48
49
# Global instance for registering user preferences
50
user_preferences_registry: UserPreferenceRegistry
51
```
52
53
### User Preference Forms
54
55
Django forms for editing user preferences with proper user context and validation.
56
57
```python { .api }
58
class UserSinglePreferenceForm(SinglePerInstancePreferenceForm):
59
"""
60
Form for editing a single user preference.
61
62
Automatically handles user instance association and
63
preference validation within user context.
64
"""
65
class Meta:
66
model = UserPreferenceModel
67
fields = '__all__'
68
69
class UserPreferenceForm(PreferenceForm):
70
"""
71
Form for editing multiple user preferences.
72
73
Provides interface for bulk editing of user preferences
74
with proper validation and user context.
75
"""
76
registry = user_preferences_registry
77
78
def user_preference_form_builder(instance, preferences=None, **kwargs):
79
"""
80
Build dynamic form for user preferences.
81
82
Args:
83
- instance: User instance
84
- preferences: List of specific preferences to include (optional)
85
- **kwargs: Additional form options
86
87
Returns:
88
Dynamic UserPreferenceForm class configured for the user
89
"""
90
```
91
92
### User Preference Admin
93
94
Django admin integration for managing user preferences with user-friendly interface.
95
96
```python { .api }
97
class UserPreferenceAdmin(PerInstancePreferenceAdmin):
98
"""
99
Admin interface for user preferences.
100
101
Attributes:
102
- search_fields: Includes "instance__username" for user search
103
- form: UserSinglePreferenceForm
104
- list_display: Shows user information along with preference details
105
"""
106
search_fields = PerInstancePreferenceAdmin.search_fields + ("instance__username",)
107
form = UserSinglePreferenceForm
108
109
def get_queryset(self, request):
110
"""Filter queryset based on user permissions."""
111
112
def has_change_permission(self, request, obj=None):
113
"""Check if user can change this preference."""
114
115
def has_delete_permission(self, request, obj=None):
116
"""Check if user can delete this preference."""
117
```
118
119
### User Preference API
120
121
REST API components for accessing user preferences through API endpoints.
122
123
```python { .api }
124
class UserPreferenceSerializer(PreferenceSerializer):
125
"""
126
Serializer for user preferences API.
127
128
Includes user context and proper permission handling
129
for user-specific preference access.
130
"""
131
132
def validate(self, attrs):
133
"""Validate user preference data."""
134
135
def update(self, instance, validated_data):
136
"""Update user preference with proper user context."""
137
138
class UserPreferencesViewSet(PerInstancePreferenceViewSet):
139
"""
140
API viewset for user preferences.
141
142
Endpoints:
143
- GET /user-preferences/ - List current user's preferences
144
- GET /user-preferences/{identifier}/ - Get specific user preference
145
- PUT /user-preferences/{identifier}/ - Update user preference
146
- POST /user-preferences/bulk/ - Bulk update user preferences
147
"""
148
queryset = UserPreferenceModel.objects.all()
149
serializer_class = UserPreferenceSerializer
150
permission_classes = [IsAuthenticated]
151
152
def get_related_instance(self):
153
"""Return current user as the related instance."""
154
return self.request.user
155
156
def get_queryset(self):
157
"""Filter preferences to current user only."""
158
return super().get_queryset().filter(instance=self.request.user)
159
```
160
161
## Usage Examples
162
163
### Defining User Preferences
164
165
```python
166
# user_preferences_registry.py (or in your app's dynamic_preferences_registry.py)
167
from dynamic_preferences.preferences import Section
168
from dynamic_preferences.users.registries import user_preferences_registry
169
from dynamic_preferences.types import BooleanPreference, StringPreference, ChoicePreference
170
171
# Define sections for user preferences
172
interface = Section('interface', 'Interface Settings')
173
notifications = Section('notifications', 'Notification Settings')
174
175
@user_preferences_registry.register
176
class Theme(ChoicePreference):
177
section = interface
178
name = 'theme'
179
default = 'light'
180
verbose_name = 'Color Theme'
181
choices = (
182
('light', 'Light Theme'),
183
('dark', 'Dark Theme'),
184
('auto', 'Auto (System)'),
185
)
186
187
@user_preferences_registry.register
188
class Language(ChoicePreference):
189
section = interface
190
name = 'language'
191
default = 'en'
192
verbose_name = 'Language'
193
choices = (
194
('en', 'English'),
195
('es', 'Spanish'),
196
('fr', 'French'),
197
)
198
199
@user_preferences_registry.register
200
class EmailNotifications(BooleanPreference):
201
section = notifications
202
name = 'email_enabled'
203
default = True
204
verbose_name = 'Email Notifications'
205
help_text = 'Receive notifications via email'
206
207
@user_preferences_registry.register
208
class NotificationFrequency(ChoicePreference):
209
section = notifications
210
name = 'frequency'
211
default = 'daily'
212
verbose_name = 'Notification Frequency'
213
choices = (
214
('immediate', 'Immediate'),
215
('daily', 'Daily Digest'),
216
('weekly', 'Weekly Summary'),
217
('never', 'Never'),
218
)
219
```
220
221
### Accessing User Preferences in Views
222
223
```python
224
from dynamic_preferences.users.registries import user_preferences_registry
225
226
def user_dashboard(request):
227
"""View that uses user preferences for personalization."""
228
if request.user.is_authenticated:
229
# Get user preferences manager
230
user_preferences = user_preferences_registry.manager(instance=request.user)
231
232
# Access user preferences
233
theme = user_preferences['interface__theme']
234
language = user_preferences['interface__language']
235
email_notifications = user_preferences['notifications__email_enabled']
236
237
# Update user preference
238
if request.method == 'POST':
239
if 'theme' in request.POST:
240
user_preferences['interface__theme'] = request.POST['theme']
241
else:
242
# Default preferences for anonymous users
243
theme = 'light'
244
language = 'en'
245
email_notifications = False
246
247
return render(request, 'dashboard.html', {
248
'theme': theme,
249
'language': language,
250
'email_notifications': email_notifications,
251
})
252
253
def user_settings(request):
254
"""View for user preference management."""
255
if not request.user.is_authenticated:
256
return redirect('login')
257
258
user_preferences = user_preferences_registry.manager(instance=request.user)
259
260
if request.method == 'POST':
261
# Update multiple preferences
262
updates = {}
263
if 'theme' in request.POST:
264
updates['interface__theme'] = request.POST['theme']
265
if 'language' in request.POST:
266
updates['interface__language'] = request.POST['language']
267
if 'email_notifications' in request.POST:
268
updates['notifications__email_enabled'] = request.POST.get('email_notifications') == 'on'
269
270
# Bulk update
271
for key, value in updates.items():
272
user_preferences[key] = value
273
274
messages.success(request, 'Settings updated successfully!')
275
return redirect('user_settings')
276
277
# Get current preferences for form
278
current_prefs = {
279
'theme': user_preferences['interface__theme'],
280
'language': user_preferences['interface__language'],
281
'email_notifications': user_preferences['notifications__email_enabled'],
282
}
283
284
return render(request, 'user_settings.html', {
285
'preferences': current_prefs,
286
})
287
```
288
289
### Using User Preference Forms
290
291
```python
292
from dynamic_preferences.users.forms import user_preference_form_builder
293
294
def user_preferences_form_view(request):
295
"""View using dynamic user preference form."""
296
if not request.user.is_authenticated:
297
return redirect('login')
298
299
# Build form for current user
300
UserPreferenceForm = user_preference_form_builder(
301
instance=request.user,
302
section='interface' # Only interface preferences
303
)
304
305
if request.method == 'POST':
306
form = UserPreferenceForm(request.POST)
307
if form.is_valid():
308
form.update_preferences()
309
messages.success(request, 'Preferences updated!')
310
return redirect('user_preferences')
311
else:
312
form = UserPreferenceForm()
313
314
return render(request, 'user_preference_form.html', {'form': form})
315
316
# Alternative: Form for specific preferences
317
def notification_settings_view(request):
318
"""View for notification-specific preferences."""
319
if not request.user.is_authenticated:
320
return redirect('login')
321
322
NotificationForm = user_preference_form_builder(
323
instance=request.user,
324
preferences=['notifications__email_enabled', 'notifications__frequency']
325
)
326
327
if request.method == 'POST':
328
form = NotificationForm(request.POST)
329
if form.is_valid():
330
form.update_preferences()
331
messages.success(request, 'Notification settings updated!')
332
return redirect('notification_settings')
333
else:
334
form = NotificationForm()
335
336
return render(request, 'notification_form.html', {'form': form})
337
```
338
339
### Template Context Processor
340
341
```python
342
# context_processors.py
343
from dynamic_preferences.users.registries import user_preferences_registry
344
345
def user_preferences(request):
346
"""Add user preferences to template context."""
347
if request.user.is_authenticated:
348
user_prefs = user_preferences_registry.manager(instance=request.user)
349
return {
350
'user_preferences': user_prefs,
351
'user_theme': user_prefs['interface__theme'],
352
}
353
return {
354
'user_preferences': None,
355
'user_theme': 'light',
356
}
357
358
# settings.py
359
TEMPLATES = [
360
{
361
'BACKEND': 'django.template.backends.django.DjangoTemplates',
362
'OPTIONS': {
363
'context_processors': [
364
# ... other context processors
365
'myapp.context_processors.user_preferences',
366
],
367
},
368
},
369
]
370
```
371
372
### API Usage
373
374
```python
375
# JavaScript example for user preference API
376
async function getUserPreferences() {
377
const response = await fetch('/api/user-preferences/', {
378
headers: {
379
'Authorization': `Bearer ${getAccessToken()}`
380
}
381
});
382
return response.json();
383
}
384
385
async function updateUserPreference(identifier, value) {
386
const response = await fetch(`/api/user-preferences/${identifier}/`, {
387
method: 'PATCH',
388
headers: {
389
'Content-Type': 'application/json',
390
'Authorization': `Bearer ${getAccessToken()}`
391
},
392
body: JSON.stringify({ value })
393
});
394
return response.json();
395
}
396
397
// Update theme preference
398
await updateUserPreference('interface__theme', 'dark');
399
400
// Bulk update preferences
401
async function bulkUpdateUserPreferences(preferences) {
402
const response = await fetch('/api/user-preferences/bulk/', {
403
method: 'POST',
404
headers: {
405
'Content-Type': 'application/json',
406
'Authorization': `Bearer ${getAccessToken()}`
407
},
408
body: JSON.stringify({ preferences })
409
});
410
return response.json();
411
}
412
```
413
414
### Custom User Preference Model
415
416
```python
417
from dynamic_preferences.users.models import UserPreferenceModel
418
419
class ExtendedUserPreference(UserPreferenceModel):
420
"""Extended user preference model with additional fields."""
421
422
created_at = models.DateTimeField(auto_now_add=True)
423
updated_at = models.DateTimeField(auto_now=True)
424
is_public = models.BooleanField(default=False)
425
426
class Meta:
427
db_table = 'extended_user_preferences'
428
429
# Update registry to use custom model
430
class ExtendedUserPreferenceRegistry(UserPreferenceRegistry):
431
preference_model = ExtendedUserPreference
432
433
extended_user_preferences_registry = ExtendedUserPreferenceRegistry()
434
435
# Register preferences with extended registry
436
@extended_user_preferences_registry.register
437
class PublicProfile(BooleanPreference):
438
section = interface
439
name = 'public_profile'
440
default = False
441
verbose_name = 'Public Profile'
442
```