Skip to content

Commit d4016d8

Browse files
authored
Add Django 5.0 support (#9233)
* Update tox.ini * Update tests for Django 5.0 * Update documentation * Update setup.py
1 parent a45432b commit d4016d8

File tree

7 files changed

+97
-85
lines changed

7 files changed

+97
-85
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ There is a live example API for testing purposes, [available here][sandbox].
5656
# Requirements
5757

5858
* Python 3.6+
59-
* Django 4.2, 4.1, 4.0, 3.2, 3.1, 3.0
59+
* Django 5.0, 4.2, 4.1, 4.0, 3.2, 3.1, 3.0
6060

6161
We **highly recommend** and only officially support the latest patch release of
6262
each Python and Django series.

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ continued development by **[signing up for a paid plan][funding]**.
8787
REST framework requires the following:
8888

8989
* Python (3.6, 3.7, 3.8, 3.9, 3.10, 3.11)
90-
* Django (3.0, 3.1, 3.2, 4.0, 4.1, 4.2)
90+
* Django (3.0, 3.1, 3.2, 4.0, 4.1, 4.2, 5.0)
9191

9292
We **highly recommend** and only officially support the latest patch release of
9393
each Python and Django series.

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ def get_version(package):
9696
'Framework :: Django :: 4.0',
9797
'Framework :: Django :: 4.1',
9898
'Framework :: Django :: 4.2',
99+
'Framework :: Django :: 5.0',
99100
'Intended Audience :: Developers',
100101
'License :: OSI Approved :: BSD License',
101102
'Operating System :: OS Independent',

tests/test_fields.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1538,7 +1538,8 @@ class TestNoOutputFormatDateTimeField(FieldValues):
15381538
field = serializers.DateTimeField(format=None)
15391539

15401540

1541-
class TestNaiveDateTimeField(FieldValues):
1541+
@override_settings(TIME_ZONE='UTC', USE_TZ=False)
1542+
class TestNaiveDateTimeField(FieldValues, TestCase):
15421543
"""
15431544
Valid and invalid values for `DateTimeField` with naive datetimes.
15441545
"""

tests/test_model_serializer.py

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import datetime
99
import decimal
1010
import json # noqa
11+
import re
1112
import sys
1213
import tempfile
1314

@@ -169,53 +170,52 @@ class Meta:
169170
model = RegularFieldsModel
170171
fields = '__all__'
171172

172-
expected = dedent("""
173-
TestSerializer():
174-
auto_field = IntegerField(read_only=True)
175-
big_integer_field = IntegerField()
176-
boolean_field = BooleanField(default=False, required=False)
177-
char_field = CharField(max_length=100)
178-
comma_separated_integer_field = CharField(max_length=100, validators=[<django.core.validators.RegexValidator object>])
179-
date_field = DateField()
180-
datetime_field = DateTimeField()
181-
decimal_field = DecimalField(decimal_places=1, max_digits=3)
182-
email_field = EmailField(max_length=100)
183-
float_field = FloatField()
184-
integer_field = IntegerField()
185-
null_boolean_field = BooleanField(allow_null=True, default=False, required=False)
186-
positive_integer_field = IntegerField()
187-
positive_small_integer_field = IntegerField()
188-
slug_field = SlugField(allow_unicode=False, max_length=100)
189-
small_integer_field = IntegerField()
190-
text_field = CharField(max_length=100, style={'base_template': 'textarea.html'})
191-
file_field = FileField(max_length=100)
192-
time_field = TimeField()
193-
url_field = URLField(max_length=100)
194-
custom_field = ModelField(model_field=<tests.test_model_serializer.CustomField: custom_field>)
195-
file_path_field = FilePathField(path=%r)
173+
expected = dedent(r"""
174+
TestSerializer\(\):
175+
auto_field = IntegerField\(read_only=True\)
176+
big_integer_field = IntegerField\(.*\)
177+
boolean_field = BooleanField\(default=False, required=False\)
178+
char_field = CharField\(max_length=100\)
179+
comma_separated_integer_field = CharField\(max_length=100, validators=\[<django.core.validators.RegexValidator object>\]\)
180+
date_field = DateField\(\)
181+
datetime_field = DateTimeField\(\)
182+
decimal_field = DecimalField\(decimal_places=1, max_digits=3\)
183+
email_field = EmailField\(max_length=100\)
184+
float_field = FloatField\(\)
185+
integer_field = IntegerField\(.*\)
186+
null_boolean_field = BooleanField\(allow_null=True, default=False, required=False\)
187+
positive_integer_field = IntegerField\(.*\)
188+
positive_small_integer_field = IntegerField\(.*\)
189+
slug_field = SlugField\(allow_unicode=False, max_length=100\)
190+
small_integer_field = IntegerField\(.*\)
191+
text_field = CharField\(max_length=100, style={'base_template': 'textarea.html'}\)
192+
file_field = FileField\(max_length=100\)
193+
time_field = TimeField\(\)
194+
url_field = URLField\(max_length=100\)
195+
custom_field = ModelField\(model_field=<tests.test_model_serializer.CustomField: custom_field>\)
196+
file_path_field = FilePathField\(path=%r\)
196197
""" % tempfile.gettempdir())
197-
198-
self.assertEqual(repr(TestSerializer()), expected)
198+
assert re.search(expected, repr(TestSerializer())) is not None
199199

200200
def test_field_options(self):
201201
class TestSerializer(serializers.ModelSerializer):
202202
class Meta:
203203
model = FieldOptionsModel
204204
fields = '__all__'
205205

206-
expected = dedent("""
207-
TestSerializer():
208-
id = IntegerField(label='ID', read_only=True)
209-
value_limit_field = IntegerField(max_value=10, min_value=1)
210-
length_limit_field = CharField(max_length=12, min_length=3)
211-
blank_field = CharField(allow_blank=True, max_length=10, required=False)
212-
null_field = IntegerField(allow_null=True, required=False)
213-
default_field = IntegerField(default=0, required=False)
214-
descriptive_field = IntegerField(help_text='Some help text', label='A label')
215-
choices_field = ChoiceField(choices=(('red', 'Red'), ('blue', 'Blue'), ('green', 'Green')))
216-
text_choices_field = ChoiceField(choices=(('red', 'Red'), ('blue', 'Blue'), ('green', 'Green')))
206+
expected = dedent(r"""
207+
TestSerializer\(\):
208+
id = IntegerField\(label='ID', read_only=True\)
209+
value_limit_field = IntegerField\(max_value=10, min_value=1\)
210+
length_limit_field = CharField\(max_length=12, min_length=3\)
211+
blank_field = CharField\(allow_blank=True, max_length=10, required=False\)
212+
null_field = IntegerField\(allow_null=True,.*required=False\)
213+
default_field = IntegerField\(default=0,.*required=False\)
214+
descriptive_field = IntegerField\(help_text='Some help text', label='A label'.*\)
215+
choices_field = ChoiceField\(choices=(?:\[|\()\('red', 'Red'\), \('blue', 'Blue'\), \('green', 'Green'\)(?:\]|\))\)
216+
text_choices_field = ChoiceField\(choices=(?:\[|\()\('red', 'Red'\), \('blue', 'Blue'\), \('green', 'Green'\)(?:\]|\))\)
217217
""")
218-
self.assertEqual(repr(TestSerializer()), expected)
218+
assert re.search(expected, repr(TestSerializer())) is not None
219219

220220
def test_nullable_boolean_field_choices(self):
221221
class NullableBooleanChoicesModel(models.Model):
@@ -1334,12 +1334,12 @@ class Meta:
13341334
}
13351335
}
13361336

1337-
expected = dedent("""
1338-
TestSerializer():
1339-
number_field = IntegerField(source='integer_field')
1337+
expected = dedent(r"""
1338+
TestSerializer\(\):
1339+
number_field = IntegerField\(.*source='integer_field'\)
13401340
""")
13411341
self.maxDiff = None
1342-
self.assertEqual(repr(TestSerializer()), expected)
1342+
assert re.search(expected, repr(TestSerializer())) is not None
13431343

13441344

13451345
class Issue6110TestModel(models.Model):

tests/test_validators.py

Lines changed: 47 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import datetime
2+
import re
23
from unittest.mock import MagicMock, patch
34

45
import pytest
6+
from django import VERSION as django_version
57
from django.db import DataError, models
68
from django.test import TestCase
79

@@ -112,11 +114,15 @@ def test_updated_instance_excluded(self):
112114
def test_doesnt_pollute_model(self):
113115
instance = AnotherUniquenessModel.objects.create(code='100')
114116
serializer = AnotherUniquenessSerializer(instance)
115-
assert AnotherUniquenessModel._meta.get_field('code').validators == []
117+
assert all(
118+
["Unique" not in repr(v) for v in AnotherUniquenessModel._meta.get_field('code').validators]
119+
)
116120

117121
# Accessing data shouldn't effect validators on the model
118122
serializer.data
119-
assert AnotherUniquenessModel._meta.get_field('code').validators == []
123+
assert all(
124+
["Unique" not in repr(v) for v in AnotherUniquenessModel._meta.get_field('code').validators]
125+
)
120126

121127
def test_related_model_is_unique(self):
122128
data = {'username': 'Existing', 'email': 'new-email@example.com'}
@@ -193,15 +199,15 @@ def setUp(self):
193199

194200
def test_repr(self):
195201
serializer = UniquenessTogetherSerializer()
196-
expected = dedent("""
197-
UniquenessTogetherSerializer():
198-
id = IntegerField(label='ID', read_only=True)
199-
race_name = CharField(max_length=100, required=True)
200-
position = IntegerField(required=True)
202+
expected = dedent(r"""
203+
UniquenessTogetherSerializer\(\):
204+
id = IntegerField\(label='ID', read_only=True\)
205+
race_name = CharField\(max_length=100, required=True\)
206+
position = IntegerField\(.*required=True\)
201207
class Meta:
202-
validators = [<UniqueTogetherValidator(queryset=UniquenessTogetherModel.objects.all(), fields=('race_name', 'position'))>]
208+
validators = \[<UniqueTogetherValidator\(queryset=UniquenessTogetherModel.objects.all\(\), fields=\('race_name', 'position'\)\)>\]
203209
""")
204-
assert repr(serializer) == expected
210+
assert re.search(expected, repr(serializer)) is not None
205211

206212
def test_is_not_unique_together(self):
207213
"""
@@ -282,13 +288,13 @@ class Meta:
282288
read_only_fields = ('race_name',)
283289

284290
serializer = ReadOnlyFieldSerializer()
285-
expected = dedent("""
286-
ReadOnlyFieldSerializer():
287-
id = IntegerField(label='ID', read_only=True)
288-
race_name = CharField(read_only=True)
289-
position = IntegerField(required=True)
291+
expected = dedent(r"""
292+
ReadOnlyFieldSerializer\(\):
293+
id = IntegerField\(label='ID', read_only=True\)
294+
race_name = CharField\(read_only=True\)
295+
position = IntegerField\(.*required=True\)
290296
""")
291-
assert repr(serializer) == expected
297+
assert re.search(expected, repr(serializer)) is not None
292298

293299
def test_read_only_fields_with_default(self):
294300
"""
@@ -366,14 +372,14 @@ class Meta:
366372
fields = ['name', 'position']
367373

368374
serializer = TestSerializer()
369-
expected = dedent("""
370-
TestSerializer():
371-
name = CharField(source='race_name')
372-
position = IntegerField()
375+
expected = dedent(r"""
376+
TestSerializer\(\):
377+
name = CharField\(source='race_name'\)
378+
position = IntegerField\(.*\)
373379
class Meta:
374-
validators = [<UniqueTogetherValidator(queryset=UniquenessTogetherModel.objects.all(), fields=('name', 'position'))>]
380+
validators = \[<UniqueTogetherValidator\(queryset=UniquenessTogetherModel.objects.all\(\), fields=\('name', 'position'\)\)>\]
375381
""")
376-
assert repr(serializer) == expected
382+
assert re.search(expected, repr(serializer)) is not None
377383

378384
def test_default_validator_with_multiple_fields_with_same_source(self):
379385
class TestSerializer(serializers.ModelSerializer):
@@ -411,13 +417,13 @@ class Meta:
411417
validators = []
412418

413419
serializer = NoValidatorsSerializer()
414-
expected = dedent("""
415-
NoValidatorsSerializer():
416-
id = IntegerField(label='ID', read_only=True)
417-
race_name = CharField(max_length=100)
418-
position = IntegerField()
420+
expected = dedent(r"""
421+
NoValidatorsSerializer\(\):
422+
id = IntegerField\(label='ID', read_only=True.*\)
423+
race_name = CharField\(max_length=100\)
424+
position = IntegerField\(.*\)
419425
""")
420-
assert repr(serializer) == expected
426+
assert re.search(expected, repr(serializer)) is not None
421427

422428
def test_ignore_validation_for_null_fields(self):
423429
# None values that are on fields which are part of the uniqueness
@@ -540,16 +546,16 @@ def test_repr(self):
540546
# the order of validators isn't deterministic so delete
541547
# fancy_conditions field that has two of them
542548
del serializer.fields['fancy_conditions']
543-
expected = dedent("""
544-
UniqueConstraintSerializer():
545-
id = IntegerField(label='ID', read_only=True)
546-
race_name = CharField(max_length=100, required=True)
547-
position = IntegerField(required=True)
548-
global_id = IntegerField(validators=[<UniqueValidator(queryset=UniqueConstraintModel.objects.all())>])
549+
expected = dedent(r"""
550+
UniqueConstraintSerializer\(\):
551+
id = IntegerField\(label='ID', read_only=True\)
552+
race_name = CharField\(max_length=100, required=True\)
553+
position = IntegerField\(.*required=True\)
554+
global_id = IntegerField\(.*validators=\[<UniqueValidator\(queryset=UniqueConstraintModel.objects.all\(\)\)>\]\)
549555
class Meta:
550-
validators = [<UniqueTogetherValidator(queryset=<QuerySet [<UniqueConstraintModel: UniqueConstraintModel object (1)>, <UniqueConstraintModel: UniqueConstraintModel object (2)>]>, fields=('race_name', 'position'))>]
556+
validators = \[<UniqueTogetherValidator\(queryset=<QuerySet \[<UniqueConstraintModel: UniqueConstraintModel object \(1\)>, <UniqueConstraintModel: UniqueConstraintModel object \(2\)>\]>, fields=\('race_name', 'position'\)\)>\]
551557
""")
552-
assert repr(serializer) == expected
558+
assert re.search(expected, repr(serializer)) is not None
553559

554560
def test_unique_together_field(self):
555561
"""
@@ -569,15 +575,18 @@ def test_single_field_uniq_validators(self):
569575
UniqueConstraint with single field must be transformed into
570576
field's UniqueValidator
571577
"""
578+
# Django 5 includes Max and Min values validators for IntergerField
579+
extra_validators_qty = 2 if django_version[0] >= 5 else 0
580+
#
572581
serializer = UniqueConstraintSerializer()
573582
assert len(serializer.validators) == 1
574583
validators = serializer.fields['global_id'].validators
575-
assert len(validators) == 1
584+
assert len(validators) == 1 + extra_validators_qty
576585
assert validators[0].queryset == UniqueConstraintModel.objects
577586

578587
validators = serializer.fields['fancy_conditions'].validators
579-
assert len(validators) == 2
580-
ids_in_qs = {frozenset(v.queryset.values_list(flat=True)) for v in validators}
588+
assert len(validators) == 2 + extra_validators_qty
589+
ids_in_qs = {frozenset(v.queryset.values_list(flat=True)) for v in validators if hasattr(v, "queryset")}
581590
assert ids_in_qs == {frozenset([1]), frozenset([3])}
582591

583592

tox.ini

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ envlist =
44
{py36,py37,py38,py39}-django31
55
{py36,py37,py38,py39,py310}-django32
66
{py38,py39,py310}-{django40,django41,django42,djangomain}
7-
{py311}-{django41,django42,djangomain}
8-
{py312}-{django42,djangomain}
7+
{py311}-{django41,django42,django50,djangomain}
8+
{py312}-{django42,djanggo50,djangomain}
99
base
1010
dist
1111
docs
@@ -23,6 +23,7 @@ deps =
2323
django40: Django>=4.0,<4.1
2424
django41: Django>=4.1,<4.2
2525
django42: Django>=4.2,<5.0
26+
django50: Django>=5.0,<5.1
2627
djangomain: https://github.com/django/django/archive/main.tar.gz
2728
-rrequirements/requirements-testing.txt
2829
-rrequirements/requirements-optionals.txt

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy