or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdform-integration.mdindex.mdmodels-validation.mdrest-framework.mdviews-urls.md

rest-framework.mddocs/

0

# Django REST Framework Integration

1

2

Django Simple Captcha provides dedicated serializers for integrating captcha validation into Django REST Framework APIs. These serializers enable captcha protection for API endpoints while maintaining the same validation logic as form-based implementations.

3

4

## Capabilities

5

6

### CaptchaSerializer

7

8

Standalone serializer for API endpoints that need captcha validation without being tied to a specific model.

9

10

```python { .api }

11

class CaptchaSerializer(serializers.Serializer):

12

captcha_code = serializers.CharField(max_length=32, write_only=True, required=True)

13

captcha_hashkey = serializers.CharField(max_length=40, write_only=True, required=True)

14

15

def run_validation(data=empty):

16

"""

17

Validate captcha data (captcha fields remain in validated data).

18

19

Parameters:

20

- data: dict, input data containing captcha_code and captcha_hashkey

21

22

Returns:

23

dict: Validated data including captcha fields

24

25

Raises:

26

ValidationError: If captcha validation fails or fields are missing

27

"""

28

```

29

30

### CaptchaModelSerializer

31

32

Model serializer that includes captcha validation for API endpoints that work with Django model instances.

33

34

```python { .api }

35

class CaptchaModelSerializer(serializers.ModelSerializer):

36

captcha_code = serializers.CharField(max_length=32, write_only=True, required=True)

37

captcha_hashkey = serializers.CharField(max_length=40, write_only=True, required=True)

38

39

def run_validation(data=empty):

40

"""

41

Validate captcha data (captcha fields remain in validated data).

42

43

Parameters:

44

- data: dict, input data containing model fields plus captcha fields

45

46

Returns:

47

dict: Validated data including captcha fields

48

49

Raises:

50

ValidationError: If captcha validation fails or model validation fails

51

"""

52

```

53

54

## Usage Examples

55

56

### Basic API View with Captcha

57

58

```python

59

from rest_framework.views import APIView

60

from rest_framework.response import Response

61

from rest_framework import status

62

from captcha.serializers import CaptchaSerializer

63

from captcha.models import CaptchaStore

64

65

class ContactAPIView(APIView):

66

def get(self, request):

67

"""Generate new captcha for the form."""

68

captcha_key = CaptchaStore.generate_key()

69

return Response({

70

'captcha_key': captcha_key,

71

'captcha_image_url': f'/captcha/image/{captcha_key}/',

72

'captcha_audio_url': f'/captcha/audio/{captcha_key}.wav',

73

})

74

75

def post(self, request):

76

"""Process contact form with captcha validation."""

77

# Create a combined serializer

78

class ContactSerializer(CaptchaSerializer):

79

name = serializers.CharField(max_length=100)

80

email = serializers.EmailField()

81

message = serializers.CharField()

82

83

serializer = ContactSerializer(data=request.data)

84

if serializer.is_valid():

85

# Captcha was validated automatically

86

contact_data = serializer.validated_data

87

# Process contact form (send email, save to database, etc.)

88

return Response({'status': 'success'}, status=status.HTTP_200_OK)

89

90

return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

91

```

92

93

### Model-Based API with Captcha

94

95

```python

96

from rest_framework import generics

97

from django.contrib.auth.models import User

98

from captcha.serializers import CaptchaModelSerializer

99

100

class UserRegistrationSerializer(CaptchaModelSerializer):

101

password = serializers.CharField(write_only=True)

102

103

class Meta:

104

model = User

105

fields = ['username', 'email', 'password', 'captcha_code', 'captcha_hashkey']

106

107

def create(self, validated_data):

108

# Captcha fields are already removed by run_validation

109

password = validated_data.pop('password')

110

user = User.objects.create_user(**validated_data)

111

user.set_password(password)

112

user.save()

113

return user

114

115

class UserRegistrationView(generics.CreateAPIView):

116

serializer_class = UserRegistrationSerializer

117

queryset = User.objects.all()

118

```

119

120

### ViewSet with Captcha Protection

121

122

```python

123

from rest_framework import viewsets, decorators

124

from rest_framework.response import Response

125

from captcha.models import CaptchaStore

126

from captcha.serializers import CaptchaModelSerializer

127

128

class CommentViewSet(viewsets.ModelViewSet):

129

queryset = Comment.objects.all()

130

131

def get_serializer_class(self):

132

if self.action == 'create':

133

# Use captcha protection for comment creation

134

class CommentCaptchaSerializer(CaptchaModelSerializer):

135

class Meta:

136

model = Comment

137

fields = ['content', 'captcha_code', 'captcha_hashkey']

138

return CommentCaptchaSerializer

139

return CommentSerializer

140

141

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

142

def captcha(self, request):

143

"""Get new captcha for comment form."""

144

captcha_key = CaptchaStore.generate_key()

145

return Response({

146

'captcha_key': captcha_key,

147

'captcha_image_url': f'/captcha/image/{captcha_key}/',

148

})

149

```

150

151

### Custom Validation Logic

152

153

```python

154

from captcha.serializers import CaptchaSerializer

155

from captcha.validators import captcha_validate

156

from django.core.exceptions import ValidationError

157

158

class CustomCaptchaSerializer(serializers.Serializer):

159

email = serializers.EmailField()

160

captcha_code = serializers.CharField(max_length=32)

161

captcha_hashkey = serializers.CharField(max_length=40)

162

163

def validate(self, data):

164

"""Custom captcha validation with additional logic."""

165

try:

166

captcha_validate(data['captcha_hashkey'], data['captcha_code'])

167

except ValidationError as e:

168

raise serializers.ValidationError({'captcha': e.message})

169

170

# Remove captcha fields from validated data

171

data.pop('captcha_code', None)

172

data.pop('captcha_hashkey', None)

173

174

# Additional custom validation

175

email = data.get('email')

176

if User.objects.filter(email=email).exists():

177

raise serializers.ValidationError({'email': 'Email already exists'})

178

179

return data

180

```

181

182

### AJAX Frontend Integration

183

184

```javascript

185

// JavaScript for API captcha integration

186

class CaptchaAPI {

187

constructor() {

188

this.captchaData = null;

189

}

190

191

async loadCaptcha() {

192

const response = await fetch('/api/contact/', {

193

method: 'GET',

194

headers: {

195

'Content-Type': 'application/json',

196

},

197

});

198

199

this.captchaData = await response.json();

200

201

// Update UI with new captcha

202

document.getElementById('captcha-image').src = this.captchaData.captcha_image_url;

203

document.getElementById('captcha-key').value = this.captchaData.captcha_key;

204

}

205

206

async submitForm(formData) {

207

// Add captcha data to form

208

formData.captcha_hashkey = this.captchaData.captcha_key;

209

formData.captcha_code = document.getElementById('captcha-input').value;

210

211

const response = await fetch('/api/contact/', {

212

method: 'POST',

213

headers: {

214

'Content-Type': 'application/json',

215

'X-CSRFToken': getCookie('csrftoken'),

216

},

217

body: JSON.stringify(formData),

218

});

219

220

if (response.ok) {

221

alert('Form submitted successfully!');

222

} else {

223

const errors = await response.json();

224

if (errors.captcha) {

225

// Refresh captcha on validation error

226

await this.loadCaptcha();

227

alert('Captcha validation failed. Please try again.');

228

}

229

}

230

}

231

}

232

233

// Usage

234

const captchaAPI = new CaptchaAPI();

235

captchaAPI.loadCaptcha();

236

```

237

238

### Batch Operations with Captcha

239

240

```python

241

from rest_framework import serializers

242

from captcha.serializers import CaptchaSerializer

243

244

class BulkCreateSerializer(CaptchaSerializer):

245

items = serializers.ListField(

246

child=serializers.DictField(),

247

min_length=1,

248

max_length=10

249

)

250

251

def create(self, validated_data):

252

# Captcha already validated, process bulk creation

253

items_data = validated_data['items']

254

created_items = []

255

256

for item_data in items_data:

257

item = MyModel.objects.create(**item_data)

258

created_items.append(item)

259

260

return created_items

261

```

262

263

## API Response Format

264

265

When captcha validation fails, the API returns standard DRF validation error format:

266

267

```json

268

{

269

"captcha_code": ["This field is required."],

270

"captcha_hashkey": ["Invalid captcha."]

271

}

272

```

273

274

Successful validation removes captcha fields from the response, returning only the relevant model/business data.

275

276

## Integration Notes

277

278

- Captcha fields are automatically marked as `write_only=True`

279

- Captcha validation occurs before model validation

280

- Failed captcha validation prevents the request from proceeding

281

- Captcha fields are removed from `validated_data` after successful validation

282

- Both serializers work with the same captcha generation and validation backend as form-based views