or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdcompatibility.mdconfiguration.mdindex.mdjwt-utilities.mdserializers.mdviews-endpoints.md

compatibility.mddocs/

0

# Compatibility Utilities

1

2

Helper functions and classes for cross-version compatibility and Django integration, including user model handling and field utilities. These utilities ensure the JWT package works across different Django and Django REST Framework versions.

3

4

## Capabilities

5

6

### User Model Utilities

7

8

Functions for handling Django user model interactions across different configurations.

9

10

```python { .api }

11

def get_username_field():

12

"""

13

Returns the username field name from the configured User model.

14

15

Returns:

16

str: Username field name (e.g., 'username', 'email', 'phone')

17

18

Notes:

19

- Uses User.USERNAME_FIELD if available

20

- Falls back to 'username' if User model is not accessible

21

- Handles cases where User model is not properly configured

22

23

Examples:

24

- Default Django User: returns 'username'

25

- Custom User with email auth: returns 'email'

26

- Custom User with phone auth: returns 'phone'

27

"""

28

29

def get_username(user):

30

"""

31

Extracts username value from a user instance.

32

33

Args:

34

user: Django user model instance

35

36

Returns:

37

str: Username value from the user instance

38

39

Notes:

40

- Uses user.get_username() method if available (Django 1.5+)

41

- Falls back to user.username attribute for older versions

42

- Handles different User model implementations

43

44

Compatibility:

45

- Django 1.4: Uses user.username attribute

46

- Django 1.5+: Uses user.get_username() method

47

"""

48

```

49

50

### Serializer Compatibility

51

52

Compatibility layer for Django REST Framework serializer changes across versions.

53

54

```python { .api }

55

class Serializer(serializers.Serializer):

56

@property

57

def object(self):

58

"""

59

Backward compatibility property for accessing validated data.

60

61

Returns:

62

dict: Validated data from the serializer

63

64

Notes:

65

- In DRF 3.0+: Returns self.validated_data

66

- Provides compatibility for code expecting .object attribute

67

- Deprecated in newer DRF versions but maintained for compatibility

68

"""

69

```

70

71

### Form Field Utilities

72

73

Custom field classes that ensure consistent behavior across framework versions.

74

75

```python { .api }

76

class PasswordField(serializers.CharField):

77

def __init__(self, *args, **kwargs):

78

"""

79

Password input field with consistent styling across DRF versions.

80

81

Args:

82

*args: Positional arguments for CharField

83

**kwargs: Keyword arguments for CharField

84

85

Features:

86

- Automatically sets input_type to 'password'

87

- Preserves existing style configuration if provided

88

- Ensures password fields are rendered with proper input type

89

90

Usage:

91

password = PasswordField(write_only=True, required=True)

92

"""

93

```

94

95

## Usage Examples

96

97

### Custom User Model Integration

98

99

```python

100

from rest_framework_jwt.compat import get_username_field, get_username

101

from django.contrib.auth import get_user_model

102

103

User = get_user_model()

104

105

# Handle different username field configurations

106

username_field = get_username_field()

107

print(f"Authentication uses field: {username_field}")

108

109

# Example with email-based authentication

110

class EmailUser(AbstractUser):

111

email = models.EmailField(unique=True)

112

USERNAME_FIELD = 'email'

113

REQUIRED_FIELDS = ['username']

114

115

# get_username_field() returns 'email'

116

# get_username(user_instance) returns user.email value

117

```

118

119

### Serializer Backward Compatibility

120

121

```python

122

from rest_framework_jwt.compat import Serializer

123

from rest_framework import serializers

124

125

class CustomAuthSerializer(Serializer): # Using compat Serializer

126

username = serializers.CharField()

127

password = serializers.CharField()

128

129

def validate(self, attrs):

130

# Validation logic here

131

return attrs

132

133

# Usage that works across DRF versions

134

serializer = CustomAuthSerializer(data=request.data)

135

if serializer.is_valid():

136

# Both work due to compatibility layer:

137

data = serializer.object # Backward compatibility

138

data = serializer.validated_data # Modern approach

139

```

140

141

### Password Field Usage

142

143

```python

144

from rest_framework_jwt.compat import PasswordField

145

from rest_framework import serializers

146

147

class LoginSerializer(serializers.Serializer):

148

username = serializers.CharField()

149

password = PasswordField(write_only=True) # Ensures password input type

150

151

# Rendered as <input type="password"> in forms

152

# Ensures consistent behavior across DRF versions

153

```

154

155

### Cross-Version User Handling

156

157

```python

158

from rest_framework_jwt.compat import get_username, get_username_field

159

from django.contrib.auth import get_user_model

160

161

def create_jwt_payload(user):

162

"""Create JWT payload compatible with any User model configuration."""

163

164

# Get username regardless of field name or Django version

165

username = get_username(user)

166

username_field = get_username_field()

167

168

payload = {

169

'user_id': user.pk,

170

'username': username,

171

username_field: username, # Include both for flexibility

172

}

173

174

return payload

175

176

def authenticate_user(credentials):

177

"""Authenticate user with dynamic username field."""

178

User = get_user_model()

179

username_field = get_username_field()

180

181

# Build authentication kwargs dynamically

182

auth_kwargs = {

183

username_field: credentials.get(username_field),

184

'password': credentials.get('password'),

185

}

186

187

return authenticate(**auth_kwargs)

188

```

189

190

### Form Integration

191

192

```python

193

from django import forms

194

from rest_framework_jwt.compat import PasswordField

195

196

class JWTAuthForm(forms.Form):

197

"""Django form using JWT-compatible password field."""

198

199

username = forms.CharField(max_length=150)

200

password = forms.CharField(widget=forms.PasswordInput)

201

202

def __init__(self, *args, **kwargs):

203

super().__init__(*args, **kwargs)

204

205

# Dynamically set username field label

206

from rest_framework_jwt.compat import get_username_field

207

username_field = get_username_field()

208

209

if username_field == 'email':

210

self.fields['username'].label = 'Email Address'

211

self.fields['username'].widget.attrs['type'] = 'email'

212

elif username_field == 'phone':

213

self.fields['username'].label = 'Phone Number'

214

self.fields['username'].widget.attrs['type'] = 'tel'

215

```

216

217

## Django Version Compatibility

218

219

### User Model Evolution

220

221

```python

222

# Django 1.4 and earlier

223

class User(models.Model):

224

username = models.CharField(max_length=30, unique=True)

225

226

# get_username() method not available

227

# Must access username attribute directly

228

229

# Django 1.5+

230

class User(AbstractBaseUser):

231

USERNAME_FIELD = 'username' # Configurable username field

232

233

def get_username(self):

234

return getattr(self, self.USERNAME_FIELD)

235

236

# Custom implementations

237

class CustomUser(AbstractUser):

238

USERNAME_FIELD = 'email' # Use email instead of username

239

240

def get_username(self):

241

return self.email

242

```

243

244

### DRF Serializer Changes

245

246

```python

247

# DRF 2.x

248

class MySerializer(serializers.Serializer):

249

def restore_object(self, attrs, instance=None):

250

# Old validation method

251

pass

252

253

# Access via .object attribute

254

serializer.object

255

256

# DRF 3.0+

257

class MySerializer(serializers.Serializer):

258

def validate(self, attrs):

259

# New validation method

260

return attrs

261

262

# Access via .validated_data attribute

263

serializer.validated_data

264

265

# Compatibility layer bridges these differences

266

```

267

268

## Error Handling

269

270

### Graceful Fallbacks

271

272

```python

273

def get_username_field():

274

try:

275

# Try to get USERNAME_FIELD from User model

276

username_field = get_user_model().USERNAME_FIELD

277

except:

278

# Fallback to default if User model is not accessible

279

username_field = 'username'

280

281

return username_field

282

283

def get_username(user):

284

try:

285

# Try modern get_username() method

286

username = user.get_username()

287

except AttributeError:

288

# Fallback to direct attribute access

289

username = user.username

290

291

return username

292

```

293

294

### Common Compatibility Issues

295

296

```python

297

# Issue: Different User model configurations

298

# Solution: Use get_username_field() and get_username()

299

300

# Issue: DRF serializer API changes

301

# Solution: Use compat.Serializer with .object property

302

303

# Issue: Password field rendering inconsistencies

304

# Solution: Use compat.PasswordField with proper input type

305

306

# Issue: Import errors across Django versions

307

# Solution: Try/except blocks with fallback implementations

308

```

309

310

## Testing Compatibility

311

312

### Multi-Version Testing

313

314

```python

315

# Test with different User model configurations

316

@override_settings(AUTH_USER_MODEL='myapp.EmailUser')

317

def test_email_based_auth(self):

318

# Test JWT with email-based user model

319

pass

320

321

@override_settings(AUTH_USER_MODEL='myapp.PhoneUser')

322

def test_phone_based_auth(self):

323

# Test JWT with phone-based user model

324

pass

325

326

# Test serializer compatibility

327

def test_serializer_object_access(self):

328

serializer = CustomSerializer(data={'test': 'data'})

329

serializer.is_valid()

330

331

# Both should work

332

data1 = serializer.object

333

data2 = serializer.validated_data

334

assert data1 == data2

335

```

336

337

## Integration Patterns

338

339

### Flexible Authentication

340

341

```python

342

from rest_framework_jwt.compat import get_username_field

343

344

class FlexibleJWTSerializer(serializers.Serializer):

345

"""Serializer that adapts to any User model configuration."""

346

347

def __init__(self, *args, **kwargs):

348

super().__init__(*args, **kwargs)

349

350

# Add username field based on User model

351

username_field = get_username_field()

352

self.fields[username_field] = serializers.CharField()

353

self.fields['password'] = PasswordField(write_only=True)

354

355

def validate(self, attrs):

356

username_field = get_username_field()

357

username = attrs.get(username_field)

358

password = attrs.get('password')

359

360

if username and password:

361

user = authenticate(**{

362

username_field: username,

363

'password': password

364

})

365

366

if user and user.is_active:

367

return {'user': user}

368

369

raise serializers.ValidationError('Invalid credentials')

370

```

371

372

The compatibility utilities ensure that JWT authentication works reliably across different Django versions, user model configurations, and DRF versions while maintaining clean, maintainable code.