or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

admin-integration.mdapi-endpoints.mdconfiguration.mdcore-system.mdindex.mdsignals.mdtemplate-integration.mdutilities.mdweb-interface.md

admin-integration.mddocs/

0

# Admin Integration

1

2

Admin interface configuration for managing notifications with proper field displays, filtering options, and bulk actions for administrative oversight and debugging.

3

4

## Capabilities

5

6

### Notification Admin Classes

7

8

Django admin configuration classes for managing notifications in the Django admin interface.

9

10

```python { .api }

11

class AbstractNotificationAdmin(admin.ModelAdmin):

12

"""Base admin class for notification models."""

13

raw_id_fields = ('recipient',)

14

list_display = ('recipient', 'actor', 'level', 'target', 'unread', 'public')

15

list_filter = ('level', 'unread', 'public', 'timestamp')

16

17

def get_queryset(self, request):

18

"""

19

Optimize queryset with prefetch_related for better performance.

20

21

Args:

22

request: Django admin request object

23

24

Returns:

25

QuerySet: Optimized queryset with prefetched actors

26

"""

27

28

class NotificationAdmin(AbstractNotificationAdmin):

29

"""Concrete admin class for Notification model."""

30

raw_id_fields = ('recipient',)

31

readonly_fields = ('action_object_url', 'actor_object_url', 'target_object_url')

32

list_display = ('recipient', 'actor', 'level', 'target', 'unread', 'public')

33

list_filter = ('level', 'unread', 'public', 'timestamp')

34

actions = [mark_unread]

35

36

def get_queryset(self, request):

37

"""

38

Get optimized queryset for admin display.

39

40

Args:

41

request: Django admin request object

42

43

Returns:

44

QuerySet: Notifications with prefetched related objects

45

"""

46

```

47

48

### Admin Actions

49

50

Custom admin actions for bulk operations on notifications.

51

52

```python { .api }

53

def mark_unread(modeladmin, request, queryset):

54

"""

55

Admin action to mark selected notifications as unread.

56

57

Args:

58

modeladmin: Admin model instance

59

request: Django admin request object

60

queryset: Selected notification objects

61

62

Returns:

63

None: Updates notifications in place

64

65

Description:

66

Bulk action that sets unread=True for selected notifications

67

"""

68

```

69

70

### Usage Examples

71

72

#### Custom Admin Configuration

73

74

```python

75

# In your admin.py

76

from django.contrib import admin

77

from django.utils.translation import gettext_lazy as _

78

from notifications.models import Notification

79

from notifications.admin import NotificationAdmin as BaseNotificationAdmin

80

81

# Extend the base admin class

82

class CustomNotificationAdmin(BaseNotificationAdmin):

83

# Add more fields to list display

84

list_display = (

85

'recipient',

86

'actor',

87

'verb',

88

'level',

89

'target',

90

'unread',

91

'public',

92

'timestamp',

93

'get_description_preview'

94

)

95

96

# Add more filter options

97

list_filter = (

98

'level',

99

'unread',

100

'public',

101

'timestamp',

102

'deleted',

103

'emailed'

104

)

105

106

# Enable search

107

search_fields = (

108

'recipient__username',

109

'recipient__email',

110

'verb',

111

'description'

112

)

113

114

# Add readonly fields

115

readonly_fields = (

116

'action_object_url',

117

'actor_object_url',

118

'target_object_url',

119

'timestamp',

120

'slug'

121

)

122

123

# Organize fields in fieldsets

124

fieldsets = (

125

('Basic Information', {

126

'fields': ('recipient', 'level', 'unread', 'public')

127

}),

128

('Activity Details', {

129

'fields': ('actor_content_type', 'actor_object_id', 'verb', 'description')

130

}),

131

('Related Objects', {

132

'fields': ('target_content_type', 'target_object_id', 'action_object_content_type', 'action_object_object_id'),

133

'classes': ('collapse',)

134

}),

135

('Metadata', {

136

'fields': ('timestamp', 'deleted', 'emailed', 'data'),

137

'classes': ('collapse',)

138

}),

139

('Admin Links', {

140

'fields': ('actor_object_url', 'target_object_url', 'action_object_url'),

141

'classes': ('collapse',)

142

})

143

)

144

145

# Custom methods for display

146

def get_description_preview(self, obj):

147

"""Show truncated description in list view."""

148

if obj.description:

149

return obj.description[:50] + '...' if len(obj.description) > 50 else obj.description

150

return '-'

151

get_description_preview.short_description = 'Description'

152

153

def get_actor_type(self, obj):

154

"""Show actor content type."""

155

return obj.actor_content_type.model

156

get_actor_type.short_description = 'Actor Type'

157

158

# Add custom actions

159

actions = ['mark_unread', 'mark_read', 'soft_delete', 'mark_as_sent']

160

161

def mark_read(self, request, queryset):

162

"""Mark selected notifications as read."""

163

updated = queryset.update(unread=False)

164

self.message_user(request, f'{updated} notifications marked as read.')

165

mark_read.short_description = 'Mark selected notifications as read'

166

167

def soft_delete(self, request, queryset):

168

"""Soft delete selected notifications."""

169

updated = queryset.update(deleted=True)

170

self.message_user(request, f'{updated} notifications soft deleted.')

171

soft_delete.short_description = 'Soft delete selected notifications'

172

173

def mark_as_sent(self, request, queryset):

174

"""Mark selected notifications as emailed."""

175

updated = queryset.update(emailed=True)

176

self.message_user(request, f'{updated} notifications marked as sent.')

177

mark_as_sent.short_description = 'Mark selected notifications as emailed'

178

179

# Unregister the default admin and register custom one

180

admin.site.unregister(Notification)

181

admin.site.register(Notification, CustomNotificationAdmin)

182

```

183

184

#### Read-Only Admin for Analytics

185

186

```python

187

from django.contrib import admin

188

from notifications.models import Notification

189

190

class NotificationAnalyticsAdmin(admin.ModelAdmin):

191

"""Read-only admin for analytics and reporting."""

192

193

list_display = (

194

'id',

195

'recipient',

196

'actor',

197

'verb',

198

'level',

199

'timestamp',

200

'unread',

201

'get_time_since_created'

202

)

203

204

list_filter = (

205

'level',

206

'unread',

207

'public',

208

'timestamp',

209

('timestamp', admin.DateFieldListFilter),

210

)

211

212

search_fields = ('recipient__username', 'verb', 'description')

213

214

# Make it read-only

215

def has_add_permission(self, request):

216

return False

217

218

def has_change_permission(self, request, obj=None):

219

return False

220

221

def has_delete_permission(self, request, obj=None):

222

return False

223

224

def get_time_since_created(self, obj):

225

"""Show time since notification was created."""

226

return obj.timesince()

227

get_time_since_created.short_description = 'Age'

228

229

# Add date hierarchy for easy browsing

230

date_hierarchy = 'timestamp'

231

232

# Show more items per page

233

list_per_page = 50

234

235

# Register as separate admin interface

236

admin.site.register(Notification, NotificationAnalyticsAdmin, name='notification_analytics')

237

```

238

239

#### Inline Admin for Related Models

240

241

```python

242

from django.contrib import admin

243

from notifications.models import Notification

244

245

class NotificationInline(admin.TabularInline):

246

"""Inline admin for showing notifications on related models."""

247

model = Notification

248

extra = 0

249

readonly_fields = ('timestamp', 'level', 'verb', 'unread')

250

fields = ('timestamp', 'level', 'verb', 'unread', 'description')

251

252

def has_add_permission(self, request, obj=None):

253

return False

254

255

# Add to User admin

256

from django.contrib.auth.admin import UserAdmin as BaseUserAdmin

257

from django.contrib.auth.models import User

258

259

class UserAdmin(BaseUserAdmin):

260

inlines = [NotificationInline]

261

262

admin.site.unregister(User)

263

admin.site.register(User, UserAdmin)

264

```

265

266

#### Advanced Filtering

267

268

```python

269

from django.contrib import admin

270

from django.utils.translation import gettext_lazy as _

271

272

class UnreadFilter(admin.SimpleListFilter):

273

"""Custom filter for unread status."""

274

title = _('read status')

275

parameter_name = 'read_status'

276

277

def lookups(self, request, model_admin):

278

return (

279

('unread', _('Unread')),

280

('read', _('Read')),

281

('recent_unread', _('Unread (last 7 days)')),

282

)

283

284

def queryset(self, request, queryset):

285

if self.value() == 'unread':

286

return queryset.filter(unread=True)

287

elif self.value() == 'read':

288

return queryset.filter(unread=False)

289

elif self.value() == 'recent_unread':

290

from django.utils import timezone

291

from datetime import timedelta

292

week_ago = timezone.now() - timedelta(days=7)

293

return queryset.filter(unread=True, timestamp__gte=week_ago)

294

295

class ActorTypeFilter(admin.SimpleListFilter):

296

"""Filter by actor model type."""

297

title = _('actor type')

298

parameter_name = 'actor_type'

299

300

def lookups(self, request, model_admin):

301

# Get unique actor content types

302

from django.contrib.contenttypes.models import ContentType

303

actor_types = Notification.objects.values_list(

304

'actor_content_type', flat=True

305

).distinct()

306

307

return [

308

(ct_id, ContentType.objects.get(id=ct_id).model.title())

309

for ct_id in actor_types

310

]

311

312

def queryset(self, request, queryset):

313

if self.value():

314

return queryset.filter(actor_content_type=self.value())

315

316

class CustomNotificationAdmin(admin.ModelAdmin):

317

list_filter = (

318

UnreadFilter,

319

ActorTypeFilter,

320

'level',

321

'public',

322

'timestamp'

323

)

324

```

325

326

#### Bulk Data Management

327

328

```python

329

from django.contrib import admin

330

from django.http import HttpResponse

331

import csv

332

333

class NotificationDataAdmin(admin.ModelAdmin):

334

"""Admin with data export and bulk management features."""

335

336

actions = [

337

'export_as_csv',

338

'bulk_mark_read',

339

'bulk_soft_delete',

340

'cleanup_old_notifications'

341

]

342

343

def export_as_csv(self, request, queryset):

344

"""Export selected notifications as CSV."""

345

response = HttpResponse(content_type='text/csv')

346

response['Content-Disposition'] = 'attachment; filename="notifications.csv"'

347

348

writer = csv.writer(response)

349

writer.writerow([

350

'ID', 'Recipient', 'Actor', 'Verb', 'Level',

351

'Timestamp', 'Unread', 'Description'

352

])

353

354

for notification in queryset:

355

writer.writerow([

356

notification.id,

357

notification.recipient.username,

358

str(notification.actor),

359

notification.verb,

360

notification.level,

361

notification.timestamp,

362

notification.unread,

363

notification.description or ''

364

])

365

366

return response

367

export_as_csv.short_description = 'Export selected notifications as CSV'

368

369

def bulk_mark_read(self, request, queryset):

370

"""Mark all selected notifications as read."""

371

count = queryset.filter(unread=True).update(unread=False)

372

self.message_user(request, f'{count} notifications marked as read.')

373

bulk_mark_read.short_description = 'Mark selected as read'

374

375

def bulk_soft_delete(self, request, queryset):

376

"""Soft delete selected notifications."""

377

count = queryset.update(deleted=True)

378

self.message_user(request, f'{count} notifications soft deleted.')

379

bulk_soft_delete.short_description = 'Soft delete selected'

380

381

def cleanup_old_notifications(self, request, queryset):

382

"""Delete notifications older than 30 days."""

383

from django.utils import timezone

384

from datetime import timedelta

385

386

cutoff_date = timezone.now() - timedelta(days=30)

387

old_notifications = queryset.filter(timestamp__lt=cutoff_date)

388

count = old_notifications.count()

389

old_notifications.delete()

390

391

self.message_user(request, f'{count} old notifications deleted.')

392

cleanup_old_notifications.short_description = 'Delete notifications older than 30 days'

393

```

394

395

#### Permission-Based Admin

396

397

```python

398

from django.contrib import admin

399

400

class PermissionBasedNotificationAdmin(admin.ModelAdmin):

401

"""Admin with permission-based access control."""

402

403

def get_queryset(self, request):

404

"""Filter notifications based on user permissions."""

405

qs = super().get_queryset(request)

406

407

if request.user.is_superuser:

408

return qs

409

elif request.user.has_perm('notifications.view_all_notifications'):

410

return qs

411

else:

412

# Only show notifications for users the admin can manage

413

managed_users = self.get_managed_users(request.user)

414

return qs.filter(recipient__in=managed_users)

415

416

def get_managed_users(self, admin_user):

417

"""Get users this admin can manage."""

418

# Example: staff can manage users in their department

419

if hasattr(admin_user, 'department'):

420

return admin_user.department.users.all()

421

return []

422

423

def has_change_permission(self, request, obj=None):

424

"""Check if user can change specific notification."""

425

if not super().has_change_permission(request, obj):

426

return False

427

428

if obj and not request.user.is_superuser:

429

# Check if admin can manage this notification's recipient

430

managed_users = self.get_managed_users(request.user)

431

return obj.recipient in managed_users

432

433

return True

434

```

435

436

#### Integration with Custom Models

437

438

```python

439

from django.contrib import admin

440

from myapp.models import BlogPost

441

from notifications.models import Notification

442

443

class BlogPostAdmin(admin.ModelAdmin):

444

"""Blog post admin with notification management."""

445

446

def save_model(self, request, obj, form, change):

447

"""Send notification when blog post is published."""

448

super().save_model(request, obj, form, change)

449

450

if not change and obj.status == 'published':

451

# Send notification to followers

452

from notifications.signals import notify

453

followers = obj.author.followers.all()

454

455

notify.send(

456

sender=obj.author,

457

recipient=followers,

458

verb='published new post',

459

target=obj,

460

description=f'New blog post: {obj.title}'

461

)

462

463

self.message_user(

464

request,

465

f'Post published and {followers.count()} followers notified.'

466

)

467

468

def get_notification_count(self, obj):

469

"""Show notification count for this post."""

470

return Notification.objects.filter(

471

target_content_type__model='blogpost',

472

target_object_id=obj.id

473

).count()

474

get_notification_count.short_description = 'Notifications'

475

476

admin.site.register(BlogPost, BlogPostAdmin)

477

```