or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

admin-integration.mdcore-models.mddjango-integration.mdforms-views.mdindex.mdpreference-types.mdregistries.mdrest-api.mdserialization.mdsignals.mduser-preferences.md

user-preferences.mddocs/

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

```