or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

admin-interface.mdform-integration.mdindex.mdmodel-integration.mdrest-framework.mdtag-operations.mdutilities-management.mdview-helpers.md

rest-framework.mddocs/

0

# REST Framework Integration

1

2

Django REST framework serializers and fields for handling tags in API endpoints. Django-taggit provides comprehensive integration with Django REST Framework, enabling seamless serialization and deserialization of tags in API responses and requests.

3

4

## Capabilities

5

6

### TagListSerializerField

7

8

A specialized serializer field for handling tag lists in REST API endpoints.

9

10

```python { .api }

11

class TagListSerializerField(serializers.ListField):

12

"""

13

Serializer field for handling tag lists.

14

15

Supports both string and list input formats, with optional

16

pretty printing and custom ordering.

17

"""

18

child = serializers.CharField()

19

20

def __init__(self, **kwargs):

21

"""

22

Initialize the field.

23

24

Parameters:

25

- pretty_print (bool): Enable pretty JSON formatting

26

- style (dict): Widget style options

27

- **kwargs: Additional field options

28

"""

29

30

def to_internal_value(self, value):

31

"""

32

Convert input to internal representation.

33

34

Parameters:

35

- value: String (JSON) or list of tag names

36

37

Returns:

38

list: List of tag name strings

39

40

Raises:

41

ValidationError: If input format is invalid

42

"""

43

44

def to_representation(self, value):

45

"""

46

Convert internal value to JSON representation.

47

48

Parameters:

49

- value: Tag manager or list of tags

50

51

Returns:

52

TagList: Formatted list of tag names

53

"""

54

```

55

56

### TaggitSerializer

57

58

A mixin serializer for models that use TaggableManager fields.

59

60

```python { .api }

61

class TaggitSerializer(serializers.Serializer):

62

"""

63

Mixin serializer for handling tagged models.

64

65

Automatically handles TagListSerializerField instances

66

during create and update operations.

67

"""

68

69

def create(self, validated_data):

70

"""

71

Create instance with tag handling.

72

73

Parameters:

74

- validated_data (dict): Validated serializer data

75

76

Returns:

77

Model instance with tags applied

78

"""

79

80

def update(self, instance, validated_data):

81

"""

82

Update instance with tag handling.

83

84

Parameters:

85

- instance: Model instance to update

86

- validated_data (dict): Validated serializer data

87

88

Returns:

89

Updated model instance with tags applied

90

"""

91

```

92

93

### TagList

94

95

A specialized list class for tag representation with pretty printing support.

96

97

```python { .api }

98

class TagList(list):

99

"""

100

List subclass with pretty printing support.

101

102

Enhances regular Python lists with JSON formatting

103

capabilities for API responses.

104

"""

105

106

def __init__(self, *args, pretty_print=True, **kwargs):

107

"""

108

Initialize TagList.

109

110

Parameters:

111

- pretty_print (bool): Enable formatted JSON output

112

"""

113

114

def __str__(self):

115

"""

116

String representation with optional pretty printing.

117

118

Returns:

119

str: JSON-formatted string representation

120

"""

121

```

122

123

## Basic Serializer Integration

124

125

### Simple Model Serializer

126

127

Basic integration with a model that has TaggableManager fields.

128

129

```python

130

from rest_framework import serializers

131

from taggit.serializers import TaggitSerializer, TagListSerializerField

132

from myapp.models import Article

133

134

class ArticleSerializer(TaggitSerializer, serializers.ModelSerializer):

135

tags = TagListSerializerField()

136

137

class Meta:

138

model = Article

139

fields = ['id', 'title', 'content', 'tags', 'created_at']

140

```

141

142

### Multiple Tag Fields

143

144

Handling models with multiple TaggableManager fields.

145

146

```python

147

class BlogPostSerializer(TaggitSerializer, serializers.ModelSerializer):

148

tags = TagListSerializerField()

149

categories = TagListSerializerField()

150

151

class Meta:

152

model = BlogPost

153

fields = ['id', 'title', 'content', 'tags', 'categories', 'published_at']

154

155

# Usage in views

156

class BlogPostViewSet(viewsets.ModelViewSet):

157

queryset = BlogPost.objects.all()

158

serializer_class = BlogPostSerializer

159

160

def get_queryset(self):

161

# Optimize queries with prefetch_related

162

return super().get_queryset().prefetch_related('tags', 'categories')

163

```

164

165

### Custom Field Configuration

166

167

Customizing TagListSerializerField behavior.

168

169

```python

170

class CustomArticleSerializer(TaggitSerializer, serializers.ModelSerializer):

171

tags = TagListSerializerField(

172

required=False,

173

allow_empty=True,

174

help_text="List of tags for this article"

175

)

176

177

# Custom ordering for tags

178

ordered_tags = TagListSerializerField(

179

source='tags',

180

order_by=['name']

181

)

182

183

class Meta:

184

model = Article

185

fields = ['id', 'title', 'content', 'tags', 'created_at']

186

```

187

188

## API Input/Output Formats

189

190

### JSON Input Formats

191

192

Different ways to send tag data to the API.

193

194

```python

195

# Example API requests

196

import requests

197

198

# List format (preferred)

199

data = {

200

'title': 'My Article',

201

'content': 'Article content...',

202

'tags': ['python', 'django', 'tutorial']

203

}

204

205

# String format (also supported)

206

data = {

207

'title': 'My Article',

208

'content': 'Article content...',

209

'tags': '["python", "django", "tutorial"]'

210

}

211

212

response = requests.post('/api/articles/', json=data)

213

```

214

215

### JSON Output Format

216

217

How tags appear in API responses.

218

219

```json

220

{

221

"id": 1,

222

"title": "My Article",

223

"content": "Article content...",

224

"tags": ["python", "django", "tutorial"],

225

"created_at": "2024-01-15T10:30:00Z"

226

}

227

```

228

229

## Advanced Serializer Patterns

230

231

### Nested Tag Information

232

233

Including additional tag information in API responses.

234

235

```python

236

class TagSerializer(serializers.ModelSerializer):

237

class Meta:

238

model = Tag

239

fields = ['name', 'slug']

240

241

class DetailedArticleSerializer(TaggitSerializer, serializers.ModelSerializer):

242

tags = TagSerializer(many=True, read_only=True)

243

tag_names = TagListSerializerField(write_only=True)

244

245

class Meta:

246

model = Article

247

fields = ['id', 'title', 'content', 'tags', 'tag_names', 'created_at']

248

249

def create(self, validated_data):

250

tag_names = validated_data.pop('tag_names', [])

251

instance = super().create(validated_data)

252

instance.tags.set(tag_names)

253

return instance

254

255

def update(self, instance, validated_data):

256

tag_names = validated_data.pop('tag_names', None)

257

instance = super().update(instance, validated_data)

258

if tag_names is not None:

259

instance.tags.set(tag_names)

260

return instance

261

```

262

263

### Tag Filtering and Search

264

265

Implementing tag-based filtering in API views.

266

267

```python

268

from django_filters import rest_framework as filters

269

from rest_framework import viewsets

270

271

class ArticleFilter(filters.FilterSet):

272

tags = filters.CharFilter(method='filter_by_tags')

273

has_tag = filters.CharFilter(field_name='tags__name', lookup_expr='iexact')

274

275

def filter_by_tags(self, queryset, name, value):

276

"""Filter by multiple tags (comma-separated)."""

277

tag_names = [tag.strip() for tag in value.split(',')]

278

return queryset.filter(tags__name__in=tag_names).distinct()

279

280

class Meta:

281

model = Article

282

fields = ['tags', 'has_tag']

283

284

class ArticleViewSet(viewsets.ModelViewSet):

285

queryset = Article.objects.all()

286

serializer_class = ArticleSerializer

287

filterset_class = ArticleFilter

288

search_fields = ['title', 'content', 'tags__name']

289

290

def get_queryset(self):

291

return super().get_queryset().prefetch_related('tags')

292

```

293

294

### Tag Statistics in APIs

295

296

Providing tag usage statistics through API endpoints.

297

298

```python

299

from django.db.models import Count

300

from rest_framework.decorators import action

301

from rest_framework.response import Response

302

303

class TagViewSet(viewsets.ReadOnlyModelViewSet):

304

queryset = Tag.objects.all()

305

serializer_class = TagSerializer

306

307

@action(detail=False, methods=['get'])

308

def popular(self, request):

309

"""Get most popular tags."""

310

popular_tags = Tag.objects.annotate(

311

usage_count=Count('tagged_items')

312

).filter(usage_count__gt=0).order_by('-usage_count')[:10]

313

314

data = [

315

{

316

'name': tag.name,

317

'slug': tag.slug,

318

'usage_count': tag.usage_count

319

}

320

for tag in popular_tags

321

]

322

return Response(data)

323

324

@action(detail=True, methods=['get'])

325

def articles(self, request, pk=None):

326

"""Get articles for a specific tag."""

327

tag = self.get_object()

328

articles = Article.objects.filter(tags=tag)

329

serializer = ArticleSerializer(articles, many=True)

330

return Response(serializer.data)

331

332

class ArticleViewSet(viewsets.ModelViewSet):

333

queryset = Article.objects.all()

334

serializer_class = ArticleSerializer

335

336

@action(detail=False, methods=['get'])

337

def by_tag(self, request):

338

"""Get articles grouped by tags."""

339

tag_name = request.query_params.get('tag')

340

if not tag_name:

341

return Response({'error': 'tag parameter required'}, status=400)

342

343

articles = Article.objects.filter(tags__name__iexact=tag_name)

344

serializer = self.get_serializer(articles, many=True)

345

return Response({

346

'tag': tag_name,

347

'count': articles.count(),

348

'articles': serializer.data

349

})

350

```

351

352

### Validation and Error Handling

353

354

Custom validation for tag input in serializers.

355

356

```python

357

from rest_framework import serializers

358

from django.core.exceptions import ValidationError

359

360

class ValidatedArticleSerializer(TaggitSerializer, serializers.ModelSerializer):

361

tags = TagListSerializerField()

362

363

class Meta:

364

model = Article

365

fields = ['id', 'title', 'content', 'tags']

366

367

def validate_tags(self, value):

368

"""Custom tag validation."""

369

if len(value) > 10:

370

raise serializers.ValidationError("Maximum 10 tags allowed")

371

372

for tag in value:

373

if len(tag) > 50:

374

raise serializers.ValidationError(f"Tag '{tag}' is too long (max 50 characters)")

375

376

if not tag.replace(' ', '').replace('-', '').isalnum():

377

raise serializers.ValidationError(f"Tag '{tag}' contains invalid characters")

378

379

return value

380

381

def validate(self, data):

382

"""Cross-field validation."""

383

tags = data.get('tags', [])

384

title = data.get('title', '')

385

386

# Ensure title doesn't conflict with tags

387

if title.lower() in [tag.lower() for tag in tags]:

388

raise serializers.ValidationError("Title cannot be the same as a tag")

389

390

return data

391

```

392

393

### Permissions and Access Control

394

395

Implementing tag-based permissions in API views.

396

397

```python

398

from rest_framework.permissions import BasePermission

399

400

class TagPermission(BasePermission):

401

"""Custom permission for tag operations."""

402

403

def has_permission(self, request, view):

404

if request.method in ['GET', 'HEAD', 'OPTIONS']:

405

return True

406

return request.user.is_authenticated

407

408

def has_object_permission(self, request, view, obj):

409

if request.method in ['GET', 'HEAD', 'OPTIONS']:

410

return True

411

412

# Only allow editing of own articles

413

return obj.author == request.user

414

415

class ArticleViewSet(viewsets.ModelViewSet):

416

queryset = Article.objects.all()

417

serializer_class = ArticleSerializer

418

permission_classes = [TagPermission]

419

420

def perform_create(self, serializer):

421

serializer.save(author=self.request.user)

422

```

423

424

### Bulk Operations

425

426

Handling bulk tag operations through the API.

427

428

```python

429

from rest_framework.decorators import action

430

from rest_framework.response import Response

431

from rest_framework import status

432

433

class ArticleViewSet(viewsets.ModelViewSet):

434

queryset = Article.objects.all()

435

serializer_class = ArticleSerializer

436

437

@action(detail=False, methods=['post'])

438

def bulk_tag(self, request):

439

"""Add tags to multiple articles."""

440

article_ids = request.data.get('article_ids', [])

441

tags = request.data.get('tags', [])

442

443

if not article_ids or not tags:

444

return Response(

445

{'error': 'article_ids and tags are required'},

446

status=status.HTTP_400_BAD_REQUEST

447

)

448

449

articles = Article.objects.filter(id__in=article_ids)

450

updated_count = 0

451

452

for article in articles:

453

article.tags.add(*tags)

454

updated_count += 1

455

456

return Response({

457

'updated_count': updated_count,

458

'tags_added': tags

459

})

460

461

@action(detail=False, methods=['post'])

462

def bulk_untag(self, request):

463

"""Remove tags from multiple articles."""

464

article_ids = request.data.get('article_ids', [])

465

tags = request.data.get('tags', [])

466

467

articles = Article.objects.filter(id__in=article_ids)

468

updated_count = 0

469

470

for article in articles:

471

article.tags.remove(*tags)

472

updated_count += 1

473

474

return Response({

475

'updated_count': updated_count,

476

'tags_removed': tags

477

})

478

```