From 18c5883be8ad3c5c5c36a5e39855d79ac80de7ca Mon Sep 17 00:00:00 2001 From: Kim Christie Date: Sat, 29 Nov 2025 13:34:04 +0000 Subject: [PATCH 01/13] Clear funding (#9831) * Clear funding * Remove premium sponsor images from README Removed several image links related to premium sponsors. --- README.md | 46 ----- docs/community/funding.md | 388 -------------------------------------- docs/index.md | 31 --- mkdocs.yml | 1 - 4 files changed, 466 deletions(-) delete mode 100644 docs/community/funding.md diff --git a/README.md b/README.md index 1594ab0ad..2e843074a 100644 --- a/README.md +++ b/README.md @@ -10,30 +10,6 @@ Full documentation for the project is available at [https://www.django-rest-fram --- -# Funding - -REST framework is a *collaboratively funded project*. If you use -REST framework commercially we strongly encourage you to invest in its -continued development by [signing up for a paid plan][funding]. - -The initial aim is to provide a single full-time position on REST framework. -*Every single sign-up makes a significant impact towards making that possible.* - -[![][sentry-img]][sentry-url] -[![][stream-img]][stream-url] -[![][spacinov-img]][spacinov-url] -[![][retool-img]][retool-url] -[![][bitio-img]][bitio-url] -[![][posthog-img]][posthog-url] -[![][cryptapi-img]][cryptapi-url] -[![][fezto-img]][fezto-url] -[![][svix-img]][svix-url] -[![][zuplo-img]][zuplo-url] - -Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry][sentry-url], [Stream][stream-url], [Spacinov][spacinov-url], [Retool][retool-url], [bit.io][bitio-url], [PostHog][posthog-url], [CryptAPI][cryptapi-url], [FEZTO][fezto-url], [Svix][svix-url], and [Zuplo][zuplo-url]. - ---- - # Overview Django REST framework is a powerful and flexible toolkit for building Web APIs. @@ -189,28 +165,6 @@ Please see the [security policy][security-policy]. [funding]: https://fund.django-rest-framework.org/topics/funding/ [sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors -[sentry-img]: https://raw.githubusercontent.com/encode/django-rest-framework/main/docs/img/premium/sentry-readme.png -[stream-img]: https://raw.githubusercontent.com/encode/django-rest-framework/main/docs/img/premium/stream-readme.png -[spacinov-img]: https://raw.githubusercontent.com/encode/django-rest-framework/main/docs/img/premium/spacinov-readme.png -[retool-img]: https://raw.githubusercontent.com/encode/django-rest-framework/main/docs/img/premium/retool-readme.png -[bitio-img]: https://raw.githubusercontent.com/encode/django-rest-framework/main/docs/img/premium/bitio-readme.png -[posthog-img]: https://raw.githubusercontent.com/encode/django-rest-framework/main/docs/img/premium/posthog-readme.png -[cryptapi-img]: https://raw.githubusercontent.com/encode/django-rest-framework/main/docs/img/premium/cryptapi-readme.png -[fezto-img]: https://raw.githubusercontent.com/encode/django-rest-framework/main/docs/img/premium/fezto-readme.png -[svix-img]: https://raw.githubusercontent.com/encode/django-rest-framework/main/docs/img/premium/svix-premium.png -[zuplo-img]: https://raw.githubusercontent.com/encode/django-rest-framework/main/docs/img/premium/zuplo-readme.png - -[sentry-url]: https://getsentry.com/welcome/ -[stream-url]: https://getstream.io/?utm_source=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage -[spacinov-url]: https://www.spacinov.com/ -[retool-url]: https://retool.com/?utm_source=djangorest&utm_medium=sponsorship -[bitio-url]: https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship -[posthog-url]: https://posthog.com?utm_source=drf&utm_medium=sponsorship&utm_campaign=open-source-sponsorship -[cryptapi-url]: https://cryptapi.io -[fezto-url]: https://www.fezto.xyz/?utm_source=DjangoRESTFramework -[svix-url]: https://www.svix.com/?utm_source=django-REST&utm_medium=sponsorship -[zuplo-url]: https://zuplo.link/django-gh - [oauth1-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-rest-framework-oauth [oauth2-section]: https://www.django-rest-framework.org/api-guide/authentication/#django-oauth-toolkit [serializer-section]: https://www.django-rest-framework.org/api-guide/serializers/#serializers diff --git a/docs/community/funding.md b/docs/community/funding.md deleted file mode 100644 index 7bca4bae4..000000000 --- a/docs/community/funding.md +++ /dev/null @@ -1,388 +0,0 @@ - - - - -# Funding - -If you use REST framework commercially we strongly encourage you to invest in its continued development by signing up for a paid plan. - -**We believe that collaboratively funded software can offer outstanding returns on investment, by encouraging our users to collectively share the cost of development.** - -Signing up for a paid plan will: - -* Directly contribute to faster releases, more features, and higher quality software. -* Allow more time to be invested in keeping the package up to date. -* Safeguard the future development of REST framework. - -REST framework continues to be open-source and permissively licensed, but we firmly believe it is in the commercial best-interest for users of the project to invest in its ongoing development. - ---- - -## What funding has enabled so far - -* The [3.4](https://www.django-rest-framework.org/community/3.4-announcement/) and [3.5](https://www.django-rest-framework.org/community/3.5-announcement/) releases, including schema generation for both Swagger and RAML, a Python client library, a Command Line client, and addressing of a large number of outstanding issues. -* The [3.6](https://www.django-rest-framework.org/community/3.6-announcement/) release, including JavaScript client library, and API documentation, complete with auto-generated code samples. -* The [3.7 release](https://www.django-rest-framework.org/community/3.7-announcement/), made possible due to our collaborative funding model, focuses on improvements to schema generation and the interactive API documentation. -* The recent [3.8 release](https://www.django-rest-framework.org/community/3.8-announcement/). -* Tom Christie, the creator of Django REST framework, working on the project full-time. -* Around 80-90 issues and pull requests closed per month since Tom Christie started working on the project full-time. -* A community & operations manager position part-time for 4 months, helping mature the business and grow sponsorship. -* Contracting development time for the work on the JavaScript client library and API documentation tooling. - ---- - -## What our sponsors and users say - -> As a developer, Django REST framework feels like an obvious and natural extension to all the great things that make up Django and it's community. Getting started is easy while providing simple abstractions which makes it flexible and customizable. Contributing and supporting Django REST framework helps ensure its future and one way or another it also helps Django, and the Python ecosystem. -> -> — José Padilla, Django REST framework contributor - -  - -> The number one feature of the Python programming language is its community. Such a community is only possible because of the Open Source nature of the language and all the culture that comes from it. Building great Open Source projects require great minds. Given that, we at Vinta are not only proud to sponsor the team behind DRF but we also recognize the ROI that comes from it. -> -> — Filipe Ximenes, Vinta Software - -  - -> It's really awesome that this project continues to endure. The code base is top notch and the maintainers are committed to the highest level of quality. -DRF is one of the core reasons why Django is top choice among web frameworks today. In my opinion, it sets the standard for rest frameworks for the development community at large. -> -> — Andrew Conti, Django REST framework user - -Sign up for a paid plan today, and help ensure that REST framework becomes a sustainable, full-time funded project. - ---- - -## Individual plan - -This subscription is recommended for individuals with an interest in seeing REST framework continue to improve. - -If you are using REST framework as a full-time employee, consider recommending that your company takes out a [corporate plan](#corporate-plans). - -
-
-
-
- {{ symbol }} - {{ rates.personal1 }} - /month{% if vat %} +VAT{% endif %} -
-
Individual
-
-
- Support ongoing development -
-
- Credited on the site -
-
- -
-
-
-
- -*Billing is monthly and you can cancel at any time.* - ---- - -## Corporate plans - -These subscriptions are recommended for companies and organizations using REST framework either publicly or privately. - -In exchange for funding you'll also receive advertising space on our site, allowing you to **promote your company or product to many tens of thousands of developers worldwide**. - -Our professional and premium plans also include **priority support**. At any time your engineers can escalate an issue or discussion group thread, and we'll ensure it gets a guaranteed response within the next working day. - -
-
-
-
- {{ symbol }} - {{ rates.corporate1 }} - /month{% if vat %} +VAT{% endif %} -
-
Basic
-
-
- Support ongoing development -
-
- Funding page ad placement -
-
- -
-
-
-
-
- {{ symbol }} - {{ rates.corporate2 }} - /month{% if vat %} +VAT{% endif %} -
-
Professional
-
-
- Support ongoing development -
-
- Sidebar ad placement -
-
- Priority support for your engineers -
-
- -
-
-
-
-
- {{ symbol }} - {{ rates.corporate3 }} - /month{% if vat %} +VAT{% endif %} -
-
Premium
-
-
- Support ongoing development -
-
- Homepage ad placement -
-
- Sidebar ad placement -
-
- Priority support for your engineers -
-
- -
-
-
- -
- -*Billing is monthly and you can cancel at any time.* - -Once you've signed up, we will contact you via email and arrange your ad placements on the site. - -For further enquires please contact funding@django-rest-framework.org. - ---- - -## Accountability - -In an effort to keep the project as transparent as possible, we are releasing [monthly progress reports](https://www.encode.io/reports/march-2018/) and regularly include financial reports and cost breakdowns. - - - - -
-
-
-

Stay up to date, with our monthly progress reports...

-
- - -
-
- - -
- -
-
-
-
- - - ---- - -## Frequently asked questions - -**Q: Can you issue monthly invoices?** -A: Yes, we are happy to issue monthly invoices. Please just email us and let us know who to issue the invoice to (name and address) and which email address to send it to each month. - -**Q: Does sponsorship include VAT?** -A: Sponsorship is VAT exempt. - -**Q: Do I have to sign up for a certain time period?** -A: No, we appreciate your support for any time period that is convenient for you. Also, you can cancel your sponsorship anytime. - -**Q: Can I pay yearly? Can I pay upfront fox X amount of months at a time?** -A: We are currently only set up to accept monthly payments. However, if you'd like to support Django REST framework and you can only do yearly/upfront payments, we are happy to work with you and figure out a convenient solution. - -**Q: Are you only looking for corporate sponsors?** -A: No, we value individual sponsors just as much as corporate sponsors and appreciate any kind of support. - ---- - -## Our sponsors - -
- - diff --git a/docs/index.md b/docs/index.md index 230aa838c..3888880fa 100644 --- a/docs/index.md +++ b/docs/index.md @@ -57,32 +57,6 @@ Some reasons you might want to use REST framework: --- -## Funding - -REST framework is a *collaboratively funded project*. If you use -REST framework commercially we strongly encourage you to invest in its -continued development by **[signing up for a paid plan][funding]**. - -*Every single sign-up helps us make REST framework long-term financially sustainable.* - - -
- -*Many thanks to all our [wonderful sponsors][sponsors], and in particular to our premium backers, [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage), [Spacinov](https://www.spacinov.com/), [Retool](https://retool.com/?utm_source=djangorest&utm_medium=sponsorship), [bit.io](https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [PostHog](https://posthog.com?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship), [CryptAPI](https://cryptapi.io), [FEZTO](https://www.fezto.xyz/?utm_source=DjangoRESTFramework), [Svix](https://www.svix.com/?utm_source=django-REST&utm_medium=sponsorship), , and [Zuplo](https://zuplo.link/django-web).* - ---- - ## Requirements REST framework requires the following: @@ -192,8 +166,6 @@ Framework. For support please see the [REST framework discussion group][group], try the `#restframework` channel on `irc.libera.chat`, or raise a question on [Stack Overflow][stack-overflow], making sure to include the ['django-rest-framework'][django-rest-framework-tag] tag. -For priority support please sign up for a [professional or premium sponsorship plan](https://fund.django-rest-framework.org/topics/funding/). - ## Security **Please report security issues by emailing security@encode.io**. @@ -246,7 +218,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [serializer-section]: api-guide/serializers#serializers [modelserializer-section]: api-guide/serializers#modelserializer [functionview-section]: api-guide/views#function-based-views -[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors [quickstart]: tutorial/quickstart.md @@ -257,10 +228,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [authentication]: api-guide/authentication.md [contributing]: community/contributing.md -[funding]: community/funding.md [group]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework [stack-overflow]: https://stackoverflow.com/ [django-rest-framework-tag]: https://stackoverflow.com/questions/tagged/django-rest-framework [security-mail]: mailto:rest-framework-security@googlegroups.com -[twitter]: https://twitter.com/_tomchristie diff --git a/mkdocs.yml b/mkdocs.yml index 010aaefe2..4608f444c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -85,5 +85,4 @@ nav: - '3.0 Announcement': 'community/3.0-announcement.md' - 'Kickstarter Announcement': 'community/kickstarter-announcement.md' - 'Mozilla Grant': 'community/mozilla-grant.md' - - 'Funding': 'community/funding.md' - 'Jobs': 'community/jobs.md' From d692b87a2be24315717944736b306753ada9cfe7 Mon Sep 17 00:00:00 2001 From: Samiul Sk Date: Sat, 29 Nov 2025 19:06:58 +0530 Subject: [PATCH 02/13] Add validation for decorator order with @api_view (#9821) * Add validation for decorator order with @api_view Raise TypeError when API policy decorators (@permission_classes, @renderer_classes, etc.) are applied after @api_view instead of before it. Fixes #9596 * Address PR review feedback: update error message wording and example - Change 'must be applied before' to 'must come after (below) the' to match DRF docs language - Fix decorator order in example to show @api_view first, then policy decorator below - Remove unnecessary f-string prefixes from non-interpolated lines - Update all test assertions to match new error message wording Addresses feedback from @browniebroke in PR #9821 --- rest_framework/decorators.py | 26 ++++++++ tests/test_decorators.py | 118 +++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) diff --git a/rest_framework/decorators.py b/rest_framework/decorators.py index 93e0751b7..a69a613ba 100644 --- a/rest_framework/decorators.py +++ b/rest_framework/decorators.py @@ -87,8 +87,26 @@ def api_view(http_method_names=None): return decorator +def _check_decorator_order(func, decorator_name): + """ + Check if an API policy decorator is being applied after @api_view. + """ + # Check if func is actually a view function (result of APIView.as_view()) + if hasattr(func, 'cls') and issubclass(func.cls, APIView): + raise TypeError( + f"@{decorator_name} must come after (below) the @api_view decorator. " + "The correct order is:\n\n" + " @api_view(['GET'])\n" + f" @{decorator_name}(...)\n" + " def my_view(request):\n" + " ...\n\n" + "See https://www.django-rest-framework.org/api-guide/views/#api-policy-decorators" + ) + + def renderer_classes(renderer_classes): def decorator(func): + _check_decorator_order(func, 'renderer_classes') func.renderer_classes = renderer_classes return func return decorator @@ -96,6 +114,7 @@ def renderer_classes(renderer_classes): def parser_classes(parser_classes): def decorator(func): + _check_decorator_order(func, 'parser_classes') func.parser_classes = parser_classes return func return decorator @@ -103,6 +122,7 @@ def parser_classes(parser_classes): def authentication_classes(authentication_classes): def decorator(func): + _check_decorator_order(func, 'authentication_classes') func.authentication_classes = authentication_classes return func return decorator @@ -110,6 +130,7 @@ def authentication_classes(authentication_classes): def throttle_classes(throttle_classes): def decorator(func): + _check_decorator_order(func, 'throttle_classes') func.throttle_classes = throttle_classes return func return decorator @@ -117,6 +138,7 @@ def throttle_classes(throttle_classes): def permission_classes(permission_classes): def decorator(func): + _check_decorator_order(func, 'permission_classes') func.permission_classes = permission_classes return func return decorator @@ -124,6 +146,7 @@ def permission_classes(permission_classes): def content_negotiation_class(content_negotiation_class): def decorator(func): + _check_decorator_order(func, 'content_negotiation_class') func.content_negotiation_class = content_negotiation_class return func return decorator @@ -131,6 +154,7 @@ def content_negotiation_class(content_negotiation_class): def metadata_class(metadata_class): def decorator(func): + _check_decorator_order(func, 'metadata_class') func.metadata_class = metadata_class return func return decorator @@ -138,6 +162,7 @@ def metadata_class(metadata_class): def versioning_class(versioning_class): def decorator(func): + _check_decorator_order(func, 'versioning_class') func.versioning_class = versioning_class return func return decorator @@ -145,6 +170,7 @@ def versioning_class(versioning_class): def schema(view_inspector): def decorator(func): + _check_decorator_order(func, 'schema') func.schema = view_inspector return func return decorator diff --git a/tests/test_decorators.py b/tests/test_decorators.py index 0c070bc10..cc7cab4d7 100644 --- a/tests/test_decorators.py +++ b/tests/test_decorators.py @@ -204,6 +204,124 @@ class DecoratorTestCase(TestCase): assert isinstance(view.cls.schema, CustomSchema) + def test_incorrect_decorator_order_permission_classes(self): + """ + If @permission_classes is applied after @api_view, we should raise a TypeError. + """ + with self.assertRaises(TypeError) as cm: + @permission_classes([IsAuthenticated]) + @api_view(['GET']) + def view(request): + return Response({}) + + assert '@permission_classes must come after (below) the @api_view decorator' in str(cm.exception) + + def test_incorrect_decorator_order_renderer_classes(self): + """ + If @renderer_classes is applied after @api_view, we should raise a TypeError. + """ + with self.assertRaises(TypeError) as cm: + @renderer_classes([JSONRenderer]) + @api_view(['GET']) + def view(request): + return Response({}) + + assert '@renderer_classes must come after (below) the @api_view decorator' in str(cm.exception) + + def test_incorrect_decorator_order_parser_classes(self): + """ + If @parser_classes is applied after @api_view, we should raise a TypeError. + """ + with self.assertRaises(TypeError) as cm: + @parser_classes([JSONParser]) + @api_view(['GET']) + def view(request): + return Response({}) + + assert '@parser_classes must come after (below) the @api_view decorator' in str(cm.exception) + + def test_incorrect_decorator_order_authentication_classes(self): + """ + If @authentication_classes is applied after @api_view, we should raise a TypeError. + """ + with self.assertRaises(TypeError) as cm: + @authentication_classes([BasicAuthentication]) + @api_view(['GET']) + def view(request): + return Response({}) + + assert '@authentication_classes must come after (below) the @api_view decorator' in str(cm.exception) + + def test_incorrect_decorator_order_throttle_classes(self): + """ + If @throttle_classes is applied after @api_view, we should raise a TypeError. + """ + class OncePerDayUserThrottle(UserRateThrottle): + rate = '1/day' + + with self.assertRaises(TypeError) as cm: + @throttle_classes([OncePerDayUserThrottle]) + @api_view(['GET']) + def view(request): + return Response({}) + + assert '@throttle_classes must come after (below) the @api_view decorator' in str(cm.exception) + + def test_incorrect_decorator_order_versioning_class(self): + """ + If @versioning_class is applied after @api_view, we should raise a TypeError. + """ + with self.assertRaises(TypeError) as cm: + @versioning_class(QueryParameterVersioning) + @api_view(['GET']) + def view(request): + return Response({}) + + assert '@versioning_class must come after (below) the @api_view decorator' in str(cm.exception) + + def test_incorrect_decorator_order_metadata_class(self): + """ + If @metadata_class is applied after @api_view, we should raise a TypeError. + """ + with self.assertRaises(TypeError) as cm: + @metadata_class(None) + @api_view(['GET']) + def view(request): + return Response({}) + + assert '@metadata_class must come after (below) the @api_view decorator' in str(cm.exception) + + def test_incorrect_decorator_order_content_negotiation_class(self): + """ + If @content_negotiation_class is applied after @api_view, we should raise a TypeError. + """ + class CustomContentNegotiation(BaseContentNegotiation): + def select_renderer(self, request, renderers, format_suffix): + return (renderers[0], renderers[0].media_type) + + with self.assertRaises(TypeError) as cm: + @content_negotiation_class(CustomContentNegotiation) + @api_view(['GET']) + def view(request): + return Response({}) + + assert '@content_negotiation_class must come after (below) the @api_view decorator' in str(cm.exception) + + def test_incorrect_decorator_order_schema(self): + """ + If @schema is applied after @api_view, we should raise a TypeError. + """ + class CustomSchema(AutoSchema): + pass + + with self.assertRaises(TypeError) as cm: + @schema(CustomSchema()) + @api_view(['GET']) + def view(request): + return Response({}) + + assert '@schema must come after (below) the @api_view decorator' in str(cm.exception) + class ActionDecoratorTestCase(TestCase): From 363dbba4137fe488f33ed24e0a9025228e66301f Mon Sep 17 00:00:00 2001 From: Abhishek Tiwari Date: Mon, 1 Dec 2025 05:01:55 -0500 Subject: [PATCH 03/13] Adding axioms-drf-py to third party permission package list (#9826) * Add axiom-drf-py package to 3rd party drf packages Add axiom-drf-py package to 3rd party drf packages * Update permissions.md to include axioms-drf-py Added information about axioms-drf-py package for authentication and authorization. * Add axiom-drf-py to third-party packages list --- docs/api-guide/permissions.md | 5 +++++ docs/community/third-party-packages.md | 2 ++ 2 files changed, 7 insertions(+) diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index c6d9f9338..f96aa18dd 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -340,6 +340,10 @@ The [Django Rest Framework Role Filters][django-rest-framework-role-filters] pac The [Django Rest Framework PSQ][drf-psq] package is an extension that gives support for having action-based **permission_classes**, **serializer_class**, and **queryset** dependent on permission-based rules. +## Axioms DRF PY + +The [Axioms DRF PY][axioms-drf-py] package is an extension that provides support for authentication and claim-based fine-grained authorization (**scopes**, **roles**, **groups**, **permissions**, etc. including object-level checks) using JWT tokens issued by an OAuth2/OIDC Authorization Server including AWS Cognito, Auth0, Okta, Microsoft Entra, etc. + [cite]: https://developer.apple.com/library/mac/#documentation/security/Conceptual/AuthenticationAndAuthorizationGuide/Authorization/Authorization.html [authentication]: authentication.md @@ -359,3 +363,4 @@ The [Django Rest Framework PSQ][drf-psq] package is an extension that gives supp [django-rest-framework-guardian]: https://github.com/rpkilby/django-rest-framework-guardian [drf-access-policy]: https://github.com/rsinger86/drf-access-policy [drf-psq]: https://github.com/drf-psq/drf-psq +[axioms-drf-py]: https://github.com/abhishektiwari/axioms-drf-py diff --git a/docs/community/third-party-packages.md b/docs/community/third-party-packages.md index 70f4f3e51..1a9e4e468 100644 --- a/docs/community/third-party-packages.md +++ b/docs/community/third-party-packages.md @@ -73,6 +73,7 @@ To submit new content, [create a pull request][drf-create-pr]. * [dry-rest-permissions][dry-rest-permissions] - Provides a simple way to define permissions for individual api actions. * [drf-access-policy][drf-access-policy] - Declarative and flexible permissions inspired by AWS' IAM policies. * [drf-psq][drf-psq] - An extension that gives support for having action-based **permission_classes**, **serializer_class**, and **queryset** dependent on permission-based rules. +* [axioms-drf-py][axioms-drf-py] - Supports authentication and claim-based fine-grained authorization (**scopes**, **roles**, **groups**, **permissions**, etc. including object-level checks) using JWT tokens issued by an OAuth2/OIDC Authorization Server. ### Serializers @@ -265,3 +266,4 @@ To submit new content, [create a pull request][drf-create-pr]. [django-pyoidc]: https://github.com/makinacorpus/django_pyoidc [apitally]: https://github.com/apitally/apitally-py [drf-shapeless-serializers]: https://github.com/khaledsukkar2/drf-shapeless-serializers +[axioms-drf-py]: https://github.com/abhishektiwari/axioms-drf-py From 1c4af77c3b989e99020214a81d53208fe0ebbf21 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 13:52:51 +0000 Subject: [PATCH 04/13] Bump actions/checkout from 5 to 6 in the github-actions group (#9834) Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 5 to 6 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/main.yml | 4 ++-- .github/workflows/mkdocs-deploy.yml | 2 +- .github/workflows/pre-commit.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 45e745ccc..8ae01735c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,7 +21,7 @@ jobs: - '3.14' steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: actions/setup-python@v6 with: @@ -53,7 +53,7 @@ jobs: name: Test documentation links runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: actions/setup-python@v6 with: diff --git a/.github/workflows/mkdocs-deploy.yml b/.github/workflows/mkdocs-deploy.yml index 7b7c5df2a..ef58e9b77 100644 --- a/.github/workflows/mkdocs-deploy.yml +++ b/.github/workflows/mkdocs-deploy.yml @@ -20,7 +20,7 @@ jobs: concurrency: group: ${{ github.workflow }}-${{ github.ref }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - run: git fetch --no-tags --prune --depth=1 origin gh-pages - uses: actions/setup-python@v6 with: diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 8432fe48b..a1b678919 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: fetch-depth: 0 From f9f10e041f9b2a2c936ee54a437d4c255f76e626 Mon Sep 17 00:00:00 2001 From: HoodyH Date: Tue, 2 Dec 2025 15:44:53 +0100 Subject: [PATCH 05/13] Bigint coerce to string (#9775) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: BinIntegerField and COERCE_BIGINT_TO_STRING setting, bigint now can have string api representation * fix: wrong import location in serializers.py * feat: updated with changes requests - typo fix - value test fix - import order fix * Update tests/test_fields.py * Update docs/api-guide/fields.md * Update docs/api-guide/fields.md * refactor: changed BigIntegerField.to_representation simplified + calling super --------- Co-authored-by: Asif Saif Uddin {"Auvi":"অভি"} --- docs/api-guide/fields.md | 12 +++++++ docs/api-guide/settings.md | 8 +++++ rest_framework/fields.py | 22 ++++++++++++ rest_framework/serializers.py | 5 +-- rest_framework/settings.py | 1 + tests/test_fields.py | 64 ++++++++++++++++++++++++++++++++++ tests/test_model_serializer.py | 2 +- 7 files changed, 111 insertions(+), 3 deletions(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index c5682c179..eb93119d0 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -269,6 +269,18 @@ Corresponds to `django.db.models.fields.IntegerField`, `django.db.models.fields. * `max_value` Validate that the number provided is no greater than this value. * `min_value` Validate that the number provided is no less than this value. +## BigIntegerField + +A biginteger representation. + +Corresponds to `django.db.models.fields.BigIntegerField`. + +**Signature**: `BigIntegerField(max_value=None, min_value=None, coerce_to_string=None)` + +* `max_value` Validate that the number provided is no greater than this value. +* `min_value` Validate that the number provided is no less than this value. +* `coerce_to_string` Set to `True` if string values should be returned for the representation, or `False` if `BigInteger` objects should be returned. Defaults to the same value as the `COERCE_BIGINT_TO_STRING` settings key, which will be `False` unless overridden. If `BigInteger` objects are returned by the serializer, then the final output format will be determined by the renderer. + ## FloatField A floating point representation. diff --git a/docs/api-guide/settings.md b/docs/api-guide/settings.md index 2a070b77e..390dbd911 100644 --- a/docs/api-guide/settings.md +++ b/docs/api-guide/settings.md @@ -371,6 +371,14 @@ When set to `True`, the serializer `DecimalField` class will return strings inst Default: `True` +#### COERCE_BIGINT_TO_STRING + +When returning biginteger objects in API representations that do not support numbers up to 2^64, it is best to return the value as a string. This avoids the loss of precision that occurs with biginteger implementations. + +When set to `True`, the serializer `BigIntegerField` class (by default) will return strings instead of `BigInteger` objects. When set to `False`, serializers will return `BigInteger` objects, which the default JSON encoder will return as numbers. + +Default: `False` + --- ## View names and descriptions diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 847ee7b19..f5009a730 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -921,6 +921,28 @@ class IntegerField(Field): return int(value) +class BigIntegerField(IntegerField): + + default_error_messages = { + 'invalid': _('A valid biginteger is required.'), + 'max_value': _('Ensure this value is less than or equal to {max_value}.'), + 'min_value': _('Ensure this value is greater than or equal to {min_value}.'), + 'max_string_length': _('String value too large.') + } + + def __init__(self, coerce_to_string=None, **kwargs): + super().__init__(**kwargs) + + if coerce_to_string is not None: + self.coerce_to_string = coerce_to_string + + def to_representation(self, value): + if getattr(self, 'coerce_to_string', api_settings.COERCE_BIGINT_TO_STRING): + return '' if value is None else str(value) + + return super().to_representation(value) + + class FloatField(Field): default_error_messages = { 'invalid': _('A valid number is required.'), diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index ea2daffd5..ca60810df 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -53,7 +53,7 @@ from rest_framework.validators import ( # This helps keep the separation between model fields, form fields, and # serializer fields more explicit. from rest_framework.fields import ( # NOQA # isort:skip - BooleanField, CharField, ChoiceField, DateField, DateTimeField, DecimalField, + BigIntegerField, BooleanField, CharField, ChoiceField, DateField, DateTimeField, DecimalField, DictField, DurationField, EmailField, Field, FileField, FilePathField, FloatField, HiddenField, HStoreField, IPAddressField, ImageField, IntegerField, JSONField, ListField, ModelField, MultipleChoiceField, ReadOnlyField, @@ -906,7 +906,8 @@ class ModelSerializer(Serializer): """ serializer_field_mapping = { models.AutoField: IntegerField, - models.BigIntegerField: IntegerField, + models.BigAutoField: BigIntegerField, + models.BigIntegerField: BigIntegerField, models.BooleanField: BooleanField, models.CharField: CharField, models.CommaSeparatedIntegerField: CharField, diff --git a/rest_framework/settings.py b/rest_framework/settings.py index 50e3ad40e..c6cc97c53 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -116,6 +116,7 @@ DEFAULTS = { 'COMPACT_JSON': True, 'STRICT_JSON': True, 'COERCE_DECIMAL_TO_STRING': True, + 'COERCE_BIGINT_TO_STRING': False, 'UPLOADED_FILES_USE_URL': True, # Browsable API diff --git a/tests/test_fields.py b/tests/test_fields.py index 03ff366ae..e36079318 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1099,6 +1099,70 @@ class TestMinMaxIntegerField(FieldValues): field = serializers.IntegerField(min_value=1, max_value=3) +class TestBigIntegerField(FieldValues): + """ + Valid and invalid values for `BigIntegerField`. + """ + valid_inputs = { + '1': 1, + '0': 0, + 1: 1, + 0: 0, + 123: 123, + -123: -123, + '999999999999999999999999999': 999999999999999999999999999, + -999999999999999999999999999: -999999999999999999999999999, + 1.0: 1, + 0.0: 0, + '1.0': 1 + } + invalid_inputs = { + 0.5: ['A valid biginteger is required.'], + 'abc': ['A valid biginteger is required.'], + '0.5': ['A valid biginteger is required.'] + } + outputs = { + '1': 1, + '0': 0, + 1: 1, + 0: 0, + 1.0: 1, + 0.0: 0, + '999999999999999999999999999': 999999999999999999999999999, + -999999999999999999999999999: -999999999999999999999999999 + } + field = serializers.BigIntegerField() + + +class TestMinMaxBigIntegerField(FieldValues): + """ + Valid and invalid values for `BigIntegerField` with min and max limits. + """ + valid_inputs = { + '1': 1, + '3': 3, + 1: 1, + 3: 3, + } + invalid_inputs = { + 0: ['Ensure this value is greater than or equal to 1.'], + 4: ['Ensure this value is less than or equal to 3.'], + '0': ['Ensure this value is greater than or equal to 1.'], + '4': ['Ensure this value is less than or equal to 3.'], + } + outputs = {} + field = serializers.BigIntegerField(min_value=1, max_value=3) + + +class TestCoercionBigIntegerField(TestCase): + + def test_force_coerce_to_string(self): + field = serializers.BigIntegerField(coerce_to_string=True) + value = field.to_representation(1) + assert isinstance(value, str) + assert value == "1" + + class TestFloatField(FieldValues): """ Valid and invalid values for `FloatField`. diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index eac51ae70..38e7f4423 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -171,7 +171,7 @@ class TestRegularFieldMappings(TestCase): expected = dedent(r""" TestSerializer\(\): auto_field = IntegerField\(read_only=True\) - big_integer_field = IntegerField\(.*\) + big_integer_field = BigIntegerField\(.*\) boolean_field = BooleanField\(required=False\) char_field = CharField\(max_length=100\) comma_separated_integer_field = CharField\(max_length=100, validators=\[\]\) From 442444f0bedc55af7ea1fcdc5755a343de1b1c57 Mon Sep 17 00:00:00 2001 From: Pravin <91125540+p-r-a-v-i-n@users.noreply.github.com> Date: Tue, 2 Dec 2025 22:02:00 +0530 Subject: [PATCH 06/13] =?UTF-8?q?Replace=20=E2=80=9Clet=E2=80=99s=E2=80=9D?= =?UTF-8?q?=20with=20=E2=80=9Clets=E2=80=9D=20where=20grammatically=20corr?= =?UTF-8?q?ect=20(#9833)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- codespell-ignore-words.txt | 3 ++- docs/api-guide/authentication.md | 2 +- docs/api-guide/serializers.md | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/codespell-ignore-words.txt b/codespell-ignore-words.txt index 7670fb785..07c9fdb9b 100644 --- a/codespell-ignore-words.txt +++ b/codespell-ignore-words.txt @@ -3,4 +3,5 @@ assertIn IAM endcode deque -thead \ No newline at end of file +thead +lets \ No newline at end of file diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index f41481310..2f4d42959 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -416,7 +416,7 @@ JSON Web Token is a fairly new standard which can be used for token-based authen ## Hawk HTTP Authentication -The [HawkREST][hawkrest] library builds on the [Mohawk][mohawk] library to let you work with [Hawk][hawk] signed requests and responses in your API. [Hawk][hawk] let's two parties securely communicate with each other using messages signed by a shared key. It is based on [HTTP MAC access authentication][mac] (which was based on parts of [OAuth 1.0][oauth-1.0a]). +The [HawkREST][hawkrest] library builds on the [Mohawk][mohawk] library to let you work with [Hawk][hawk] signed requests and responses in your API. [Hawk][hawk] lets two parties securely communicate with each other using messages signed by a shared key. It is based on [HTTP MAC access authentication][mac] (which was based on parts of [OAuth 1.0][oauth-1.0a]). ## HTTP Signature Authentication diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index e73257381..8d4da4ee4 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -430,7 +430,7 @@ The context dictionary can be used within any serializer field logic, such as a Often you'll want serializer classes that map closely to Django model definitions. -The `ModelSerializer` class provides a shortcut that let's you automatically create a `Serializer` class with fields that correspond to the Model fields. +The `ModelSerializer` class provides a shortcut that lets you automatically create a `Serializer` class with fields that correspond to the Model fields. **The `ModelSerializer` class is the same as a regular `Serializer` class, except that**: From fc17a60b2d6d447bd5698e61cd1605e82f8a8ec9 Mon Sep 17 00:00:00 2001 From: Pravin <91125540+p-r-a-v-i-n@users.noreply.github.com> Date: Wed, 3 Dec 2025 15:52:25 +0530 Subject: [PATCH 07/13] Use pyproject.toml for package data, clean MANIFEST.in (#9825) --- MANIFEST.in | 5 ----- pyproject.toml | 8 ++++++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index f7b975e6f..3d0ca3745 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,8 +1,3 @@ -include README.md -include LICENSE.md recursive-include tests/ * -recursive-include rest_framework/static *.js *.css *.map *.png *.ico *.eot *.svg *.ttf *.woff *.woff2 -recursive-include rest_framework/templates *.html schema.js -recursive-include rest_framework/locale *.mo global-exclude __pycache__ global-exclude *.py[co] diff --git a/pyproject.toml b/pyproject.toml index c89b6a002..37fef3bd2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,6 +7,7 @@ name = "djangorestframework" description = "Web APIs for Django, made easy." readme = "README.md" license = "BSD-3-Clause" +license-files = [ "LICENSE.md" ] authors = [ { name = "Tom Christie", email = "tom@tomchristie.com" } ] requires-python = ">=3.10" classifiers = [ @@ -44,6 +45,13 @@ version = { attr = "rest_framework.__version__" } [tool.setuptools.packages.find] include = [ "rest_framework*" ] +[tool.setuptools.package-data] +"rest_framework" = [ + "templates/**/*", + "static/**/*", + "locale/**/*.mo", +] + [tool.isort] skip = [ ".tox" ] atomic = true From e027f0cb77dfa0103c944ad6cdcce51270d9ed32 Mon Sep 17 00:00:00 2001 From: Shrikant Sudam Giri <91122319+Shrikantgiri25@users.noreply.github.com> Date: Fri, 5 Dec 2025 14:15:01 +0530 Subject: [PATCH 08/13] Fix Windows test failures and remove debug prints for DRF (#9808) --- tests/schemas/test_openapi.py | 9 --------- tests/test_model_serializer.py | 2 ++ 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/tests/schemas/test_openapi.py b/tests/schemas/test_openapi.py index a168cb466..13746bb90 100644 --- a/tests/schemas/test_openapi.py +++ b/tests/schemas/test_openapi.py @@ -257,7 +257,6 @@ class TestOperationIntrospection(TestCase): inspector.view = view request_body = inspector.get_request_body(path, method) - print(request_body) assert request_body['content']['application/json']['schema']['$ref'] == '#/components/schemas/Item' components = inspector.get_components(path, method) @@ -928,7 +927,6 @@ class TestOperationIntrospection(TestCase): request = create_request('/') schema = generator.get_schema(request=request) schema_str = str(schema) - print(schema_str) assert schema_str.count("operationId") == 2 assert schema_str.count("newExample") == 1 assert schema_str.count("oldExample") == 1 @@ -948,7 +946,6 @@ class TestOperationIntrospection(TestCase): assert len(w) == 1 assert issubclass(w[-1].category, UserWarning) - print(str(w[-1].message)) assert 'You have a duplicated operationId' in str(w[-1].message) def test_operation_id_viewset(self): @@ -960,7 +957,6 @@ class TestOperationIntrospection(TestCase): request = create_request('/') schema = generator.get_schema(request=request) - print(schema) assert schema['paths']['/account/']['get']['operationId'] == 'listExampleViewSets' assert schema['paths']['/account/']['post']['operationId'] == 'createExampleViewSet' assert schema['paths']['/account/{id}/']['get']['operationId'] == 'retrieveExampleViewSet' @@ -1284,8 +1280,6 @@ class TestGenerator(TestCase): request = create_request('/') schema = generator.get_schema(request=request) - print(schema) - assert 'components' in schema assert 'schemas' in schema['components'] assert 'ExampleModel' in schema['components']['schemas'] @@ -1299,8 +1293,6 @@ class TestGenerator(TestCase): request = create_request('/') schema = generator.get_schema(request=request) - print(schema) - route = schema['paths']['/api-token-auth/']['post'] body_schema = route['requestBody']['content']['application/json']['schema'] @@ -1327,7 +1319,6 @@ class TestGenerator(TestCase): request = create_request('/') schema = generator.get_schema(request=request) - print(schema) assert 'components' in schema assert 'schemas' in schema['components'] assert 'Ulysses' in schema['components']['schemas'] diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 38e7f4423..60820df4f 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -9,6 +9,7 @@ import datetime import decimal import json # noqa import re +import sys import tempfile import pytest @@ -159,6 +160,7 @@ class TestModelSerializer(TestCase): class TestRegularFieldMappings(TestCase): + @pytest.mark.skipif(sys.platform.startswith("win"), reason="Test not supported on Windows") def test_regular_fields(self): """ Model fields should map to their equivalent serializer fields. From 7eed4ab34def8ba16e196570c1766ac395279437 Mon Sep 17 00:00:00 2001 From: Bruno Alla Date: Fri, 5 Dec 2025 10:53:33 +0100 Subject: [PATCH 09/13] Add upper bounds to tox targets (#9838) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To avoid them breaking when a new version of Django comes out Co-authored-by: Asif Saif Uddin {"Auvi":"অভি"} --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 01a959455..b27c13d0f 100644 --- a/tox.ini +++ b/tox.ini @@ -27,13 +27,13 @@ deps = [testenv:base] ; Ensure optional dependencies are not required deps = - django + django<6.0 -rrequirements/requirements-testing.txt [testenv:dist] commands = python -W error::DeprecationWarning -W error::PendingDeprecationWarning runtests.py --no-pkgroot --staticfiles {posargs} deps = - django + django<6.0 -rrequirements/requirements-testing.txt -rrequirements/requirements-optionals.txt From 0fbaebc3ddeb1ee27a6d34a98d40eb90e034c680 Mon Sep 17 00:00:00 2001 From: Mehraz Hossain Rumman <59512321+MehrazRumman@users.noreply.github.com> Date: Fri, 5 Dec 2025 16:30:00 +0600 Subject: [PATCH 10/13] Add Django 6.0 support (#9819) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * adding django6.0a support * test fixed * gitignore updated * pre-commit applied * test issue 1386 fixed * fixing test issue 1386 * .gov domain handled for different version * correct urls updated * Modify URL test cases for Django version compatibility Adjust URL test cases based on Django version. * Add Django import to test_templatetags.py * Reorder import statements in test file * Update tox.ini * Update tox.ini * Revert unnecessary changes in tests * Default to AutoField for now * Add Django upper bound to base and dist tests to avoid testing on unsupported versions --------- Co-authored-by: Asif Saif Uddin {"Auvi":"অভি"} Co-authored-by: Bruno Alla --- tests/conftest.py | 1 + tests/test_templatetags.py | 3 ++- tests/test_validators.py | 2 +- tox.ini | 11 ++++++----- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 01914ae77..11a079f48 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -15,6 +15,7 @@ def pytest_configure(config): settings.configure( DEBUG_PROPAGATE_EXCEPTIONS=True, + DEFAULT_AUTO_FIELD="django.db.models.AutoField", DATABASES={ 'default': { 'ENGINE': 'django.db.backends.sqlite3', diff --git a/tests/test_templatetags.py b/tests/test_templatetags.py index 7134c1bae..8260fc839 100644 --- a/tests/test_templatetags.py +++ b/tests/test_templatetags.py @@ -1,7 +1,7 @@ import unittest from django.template import Context, Template -from django.test import TestCase +from django.test import TestCase, override_settings from django.utils.html import urlize from rest_framework.compat import coreapi, coreschema @@ -240,6 +240,7 @@ class Issue1386Tests(TestCase): Covers #1386 """ + @override_settings(URLIZE_ASSUME_HTTPS=True) def test_issue_1386(self): """ Test function urlize with different args diff --git a/tests/test_validators.py b/tests/test_validators.py index 79d4c0cf8..96354b9b1 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -752,7 +752,7 @@ class TestUniqueConstraintValidation(TestCase): validators = serializer.fields['fancy_conditions'].validators assert len(validators) == 2 + extra_validators_qty - ids_in_qs = {frozenset(v.queryset.values_list(flat=True)) for v in validators if hasattr(v, "queryset")} + ids_in_qs = {frozenset(v.queryset.values_list('id', flat=True)) for v in validators if hasattr(v, "queryset")} assert ids_in_qs == {frozenset([1]), frozenset([3])} def test_nullable_unique_constraint_fields_are_not_required(self): diff --git a/tox.ini b/tox.ini index b27c13d0f..6980d0bfa 100644 --- a/tox.ini +++ b/tox.ini @@ -2,9 +2,9 @@ envlist = {py310}-{django42,django51,django52} {py311}-{django42,django51,django52} - {py312}-{django42,django51,django52,djangomain} - {py313}-{django51,django52,djangomain} - {py314}-{django52,djangomain} + {py312}-{django42,django51,django52,django60,djangomain} + {py313}-{django51,django52,django60,djangomain} + {py314}-{django52,django60,djangomain} base dist docs @@ -20,6 +20,7 @@ deps = django50: Django>=5.0,<5.1 django51: Django>=5.1,<5.2 django52: Django>=5.2,<6.0 + django60: Django>=6.0,<6.1 djangomain: https://github.com/django/django/archive/main.tar.gz -rrequirements/requirements-testing.txt -rrequirements/requirements-optionals.txt @@ -27,13 +28,13 @@ deps = [testenv:base] ; Ensure optional dependencies are not required deps = - django<6.0 + django<6.1 -rrequirements/requirements-testing.txt [testenv:dist] commands = python -W error::DeprecationWarning -W error::PendingDeprecationWarning runtests.py --no-pkgroot --staticfiles {posargs} deps = - django<6.0 + django<6.1 -rrequirements/requirements-testing.txt -rrequirements/requirements-optionals.txt From 2da99035ccdaa14ff9c0df127dbdf09549ed7cde Mon Sep 17 00:00:00 2001 From: Pravin <91125540+p-r-a-v-i-n@users.noreply.github.com> Date: Fri, 5 Dec 2025 16:53:20 +0530 Subject: [PATCH 11/13] - Update README.md to include Django 6.0 in the requirements section. (#9839) - Add Django 6.0 to the supported Django versions in pyproject.toml. --- README.md | 2 +- pyproject.toml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2e843074a..f875f65dd 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Some reasons you might want to use REST framework: # Requirements * Python 3.10+ -* Django 4.2, 5.0, 5.1, 5.2 +* Django 4.2, 5.0, 5.1, 5.2, 6.0 We **highly recommend** and only officially support the latest patch release of each Python and Django series. diff --git a/pyproject.toml b/pyproject.toml index 37fef3bd2..da608a26c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ classifiers = [ "Framework :: Django :: 5.0", "Framework :: Django :: 5.1", "Framework :: Django :: 5.2", + "Framework :: Django :: 6.0", "Intended Audience :: Developers", "Operating System :: OS Independent", "Programming Language :: Python", From 5019582178c579dd9ed07fe8b25750e313c5c042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Pinheiro=20Reis?= Date: Fri, 5 Dec 2025 08:59:35 -0300 Subject: [PATCH 12/13] Update Brazilian Portuguese translations (#9828) Co-authored-by: Bruno Alla Co-authored-by: Bruno Alla --- .../locale/pt_BR/LC_MESSAGES/django.mo | Bin 12750 -> 13073 bytes .../locale/pt_BR/LC_MESSAGES/django.po | 83 +++++++++--------- 2 files changed, 42 insertions(+), 41 deletions(-) diff --git a/rest_framework/locale/pt_BR/LC_MESSAGES/django.mo b/rest_framework/locale/pt_BR/LC_MESSAGES/django.mo index 03a0651fa73cd954a0ade4336860db8319a9d3c6..cf9f4c27c16278d23a7bf7536631cf02e6385aee 100644 GIT binary patch delta 3702 zcmajheQZ_r9mnzC1(cSD(t;~pf%dc%T4;OeMGFN4d48Tapo()s5BHw72kt$mJ@Er;V`xLfOk&(PV@~3q#`8gYc!Dt&9>qEMEJpDX zR^TUCgB25t-*3hl^w(lD_8_;Ihp`<)Y{Qq4SLDsR9IWQV2e=-WOe#L;0aX79+<fe!kVgLW|kvkFmY7!221A;p$70h?83`<2TtQ+mA#fqzE+Oj&iYChAZ%uo(F>d-$+0 zhm6r2uO|Q0n|ZEu;)keWIgcu~U!XGZcPzs>(~CxtQ)V&VjCUe`rkjt&m_lXZDZCYb zjhe^=Dpj@AjOzO}M9Iy9ln72{#D$L-RxQIo@Y??XMV+jitTMwwN}!RU8s@w zptjGGsFc5sDymzUzIM+>RO;`+dAJ9c;89ctMsOE?h}&={4_}Huz)Ie4{=mUXd=K@- z7@J+G-;a9X6Q~DYLVi2URn$^U;-reL9+%-Z)J(sIoAE8=H_F7A?krr5>h~a*%pm5O zw0WF^Zajx9r>Sl&{ziA=P4xF78^H7eC!=BmWxFFef_kJn~(070YohrLF;Vpng~u;mx=eE!=@CFoVj}FeHa~lINg|gJD$4eukBJ8I`hkP-|7j^lrn2sLb@0 z97Mg~2 zpKFLTp;@V^a7UZgTqiz4_LCJ%tS~#pe?=SpsM#p#PgSLZ65r>GC zgqn(~icnFFwj>8*i3E`$?jr(1?Ev8rs-@AE<6s}LiP%f1PVz&1Pz!}^v>R0{gM@xa zROxEB6L%6SNL8(xwxb&LRM-hR9!IR#!N0ZXH}qA6JwzkHj;l4j9H>nrrVzD+Dty)O zoC$8#hPdx#94})MPCD**3D@g0Dckj|(er^bn03+_tJe=wX0+pay*{1vfbXRpOTF;1 z8HXl!w#Q=au`Vli+tQAXux{q`uFd|)lfHGg8_)QGb(ib)J8s}x+Z{J;-M-Ja{aoS7 zK!+#!p_(XC^vE<$@TDb{rura>#vQe@cMnr^R3j#iGyy+53e=Ugr^!`8sntG zS7*;Fv${Pu?(>@O&u%H63pcGx4>*CHvkQ-Gvr=~23Y@sJ*U1)N;#mEDU`NfT?z4(# zKXrR}wy8PN7QWupv$=S0J2@k%7HM_%@qCvwjy#`pl9rRQjvTPv%n%do%X$fa^zr}k zl`uZX3oGW9hrgTi;J2I0rr0S~j7((g6szfkd*)V$56wLlzT7lD(l)%c`MJp0z_r7F zv>cD5!*9;JTE_aOv&oDdeq;W3XRmc~TE=)X>jqY1mh5^wr;*t-4%q4R0Y6BDJ6i8A zPdiB`?)sh`{B0=&ZWy*R-aVmrWMtoN=tUof`1NW6_y)TJXjrtgN%A*L9MKp+=?UhFhwe zw6f2p|No6Meaof{{`&*NngvY@{xu$!#}2HVeb_CmGIgctkDOFRU%xBdzu<3Sf5*v) z6IRBK2`A15VM}EkbN#GE!o0XUU?+|CQ#LtrB5(`C(av(yZsF-h8!AZI YbY66rluhCN@2hM?25WNR^Rb410whXhC;$Ke delta 3413 zcmaLZe{5A}9mnyf6zC6Dpy*mCv_8A#N4x%@9h5?W0fQC>jKcUehJ~KqdrFVq-qW6Q zZv|xO9M%2hm^)+3l1*gukAbFH(!`i8k&s3=Gi8}BTa3EMTuh8{Y8DbTI(>icJyl}N zp4{(!J*pD*E!)=+MY#YS9#yRj8V za6Vqb#W;!j{_D6DKg3mN<{3lRG++y^!De)jTa240=-kJFXRrr7!H6fvdO(Z=fb{ zGd})FY+_;I20f@7K8DNj`>4$P8o9SAuFS8@RHBYAK}}!_uE(>u1*dR3_ctHYX~j>ckwb#z}0z_wg<)`HV4lV5+YT^f|8h8(@@Ly=L ziUD&nomJ$2Hl023f&HjrNug5bqcU(2OYw)XH;|WkkGHw_5mw_Z)E-wbP$s%@4UVA} zb`@1iugBvBi^;!I+RSu0X7-^@97I*|d0dB=uoK^n$McsIR@{v`uOBU*LrwSvR0e;F zb8sQ)(L}3|`Zl%LgpK2L+UXp|WB52KBR7%nn%|*TuDfVlhAmi+_uv66!wFR8oGO`W+y&~FQT4`TSyrg%d@E!ccL=f7kdG9o$I(+&wnvzu^6)rnXKtYJr$2) z30}ZPd=@w1byNmE!CqWPy5!fe3*SY43QQ%-zYi0*0bSHYE}^#WZEWQJW;PEuKLMr{ zwG~@YMRX81;(0uZ*KjvhGaKDt0JUXdJbn^+najMfIP)eR#y_JL*h6~sxr=k~0*+JA z<{}*yVpoQx@g`swTpC{1%c;^B2^FmhkhZ1$CfS zejN2Xa5i=lb=?nI$-lmEC;8Te+fiRUh}wcPymjLh)Qtaxn&>T@hyOtRK9urnqX}-o zQ+O6j@dH%G{(uXyi~`V>)u6VbbDYjvIu4fMGpGxH3zeDg;vIM$i*T4IAyilc#6ChT zTOhv}U07`^aatXkSHOG;Rpfod2}1RkZ`yY?twJ}PX#F zxSL1Y@p)o1p@oza{X`8hix?!x#CZP4Mk(zjHWEtnLxfMvv>rOUi2LIM594m4l1RkI z0@NOFCseGPh%LlS(;pl)JsEomJ;JqGzbaFW44eH#NH|0_p~pvUXMz0tN#3=`{4p~N z2t6TcBZO*YrfFMM8`Pv3A`TFvgqn&{t!*a%#SB!@$A||BHPykPe6R5LoQEL)e}H~W z9w5FLe^!Cph$Hb>m959ujmK$RK~%-#MVKMx6B}~z!5y(GLbdgTDmFz>IZv+!?#+v|s3LO?w%C%yXv7YnzIqb#?1YMx4O0x#4J}?!?mGq#p!sj-hpf zz_%&ahHfzCrTk#(_jNTz(W`dhEGHYCv`tg@+JDZPJ8XyDj32$6SQa_;*D5(b?Pdcf zw1+cZ(oeZjE9XrhV0Ur$H?;Ia=?T{Zh zt>*uJqwwMXela?>Vpa5&75&9KyfA87Su>~A6ndF?w_PQ=ymDeWbMpqhq?6*-?ywzj zLU-L->t*um&pIqB>qdWDX``!+<;ZCmYaqn@^pOIeMZqUNH`_A5nx VbhG`*c_}v;, 2017 # Gabriel Mitelman Tkacz , 2024 # Matheus Oliveira , 2025 +# João Victor Pinheiro Reis , 2025 msgid "" msgstr "" "Project-Id-Version: Django REST framework\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-10-13 21:45+0200\n" -"PO-Revision-Date: 2020-10-13 19:45+0000\n" -"Last-Translator: Xavier Ordoquy \n" +"POT-Creation-Date: 2025-11-18 17:00+0300\n" +"PO-Revision-Date: 2025-11-18 14:00+0000\n" +"Last-Translator: João Victor Pinheiro Reis \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/django-rest-framework-1/django-rest-framework/language/pt_BR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -35,11 +36,11 @@ msgstr "Cabeçalho básico inválido. String de credenciais não deve incluir es #: authentication.py:83 msgid "Invalid basic header. Credentials not correctly base64 encoded." -msgstr "Cabeçalho básico inválido. Credenciais codificadas em base64 incorretamente." +msgstr "Cabeçalho básico inválido. Credenciais não foram corretamente codificadas em base64." #: authentication.py:101 msgid "Invalid username/password." -msgstr "Usuário ou senha inválido." +msgstr "Usuário ou senha inválidos." #: authentication.py:104 authentication.py:206 msgid "User inactive or deleted." @@ -56,7 +57,7 @@ msgstr "Cabeçalho de token inválido. String de token não deve incluir espaço #: authentication.py:193 msgid "" "Invalid token header. Token string should not contain invalid characters." -msgstr "Cabeçalho de token inválido. String de token não deve possuir caracteres inválidos." +msgstr "Cabeçalho de token inválido. String de token não deveria possuir caracteres inválidos." #: authentication.py:203 msgid "Invalid token." @@ -88,7 +89,7 @@ msgstr "Tokens" #: authtoken/serializers.py:9 msgid "Username" -msgstr "Nome do usuário" +msgstr "Nome de usuário" #: authtoken/serializers.py:13 msgid "Password" @@ -100,11 +101,11 @@ msgstr "Impossível fazer login com as credenciais fornecidas." #: authtoken/serializers.py:38 msgid "Must include \"username\" and \"password\"." -msgstr "Obrigatório incluir \"usuário\" e \"senha\"." +msgstr "Deve incluir \"username\" e \"password\"." #: exceptions.py:102 msgid "A server error occurred." -msgstr "Ocorreu um erro de servidor." +msgstr "Um erro de servidor ocorreu." #: exceptions.py:142 msgid "Invalid input." @@ -142,21 +143,21 @@ msgstr "Não foi possível satisfazer a requisição do cabeçalho Accept." #: exceptions.py:212 #, python-brace-format msgid "Unsupported media type \"{media_type}\" in request." -msgstr "Tipo de mídia \"{media_type}\" no pedido não é suportado." +msgstr "Tipo de mídia \"{media_type}\" não é suportado." #: exceptions.py:223 msgid "Request was throttled." -msgstr "Pedido foi limitado." +msgstr "Pedido foi suprimido." #: exceptions.py:224 #, python-brace-format msgid "Expected available in {wait} second." -msgstr "Disponível em {wait} segundo." +msgstr "Espera-se que esteja diponível em {wait} segundo." #: exceptions.py:225 #, python-brace-format msgid "Expected available in {wait} seconds." -msgstr "Disponível em {wait} segundos." +msgstr "Espera-se que esteja diponível em {wait} segundos." #: fields.py:316 relations.py:245 relations.py:279 validators.py:90 #: validators.py:183 @@ -165,7 +166,7 @@ msgstr "Este campo é obrigatório." #: fields.py:317 msgid "This field may not be null." -msgstr "Este campo não pode ser nulo." +msgstr "Este campo pode não ser nulo." #: fields.py:701 msgid "Must be a valid boolean." @@ -177,7 +178,7 @@ msgstr "Não é uma string válida." #: fields.py:767 msgid "This field may not be blank." -msgstr "Este campo não pode estar em branco." +msgstr "Este campo pode não estar em branco." #: fields.py:768 fields.py:1881 #, python-brace-format @@ -201,13 +202,13 @@ msgstr "Este valor não corresponde ao padrão exigido." msgid "" "Enter a valid \"slug\" consisting of letters, numbers, underscores or " "hyphens." -msgstr "Insira um \"slug\" válido que consista de letras, números, sublinhados ou hífens." +msgstr "Insira um \"slug\" válido que consista em letras, números, sublinhados ou hífens." #: fields.py:839 msgid "" "Enter a valid \"slug\" consisting of Unicode letters, numbers, underscores, " "or hyphens." -msgstr "Insira um \"slug\" válido que consista de letras Unicode, números, sublinhados ou hífens." +msgstr "Insira um \"slug\" válido que consista em letras, números, sublinhados ou hífens Unicode." #: fields.py:854 msgid "Enter a valid URL." @@ -268,7 +269,7 @@ msgstr "Formato inválido para data e hora. Use um dos formatos a seguir: {forma #: fields.py:1149 msgid "Expected a datetime but got a date." -msgstr "Necessário uma data e hora mas recebeu uma data." +msgstr "Esperava data e hora, mas recebeu data." #: fields.py:1150 #, python-brace-format @@ -282,11 +283,11 @@ msgstr "Valor de data e hora fora do intervalo." #: fields.py:1236 #, python-brace-format msgid "Date has wrong format. Use one of these formats instead: {format}." -msgstr "Formato inválido para data. Use um dos formatos a seguir: {format}." +msgstr "Formato de data inválido. Use um dos formatos a seguir: {format}." #: fields.py:1237 msgid "Expected a date but got a datetime." -msgstr "Necessário uma data mas recebeu uma data e hora." +msgstr "Esperava data, mas recebeu data e hora." #: fields.py:1303 #, python-brace-format @@ -301,7 +302,7 @@ msgstr "Formato inválido para duração. Use um dos formatos a seguir: {format} #: fields.py:1399 fields.py:1456 #, python-brace-format msgid "\"{input}\" is not a valid choice." -msgstr "\"{input}\" não é um escolha válida." +msgstr "\"{input}\" não é uma escolha válida." #: fields.py:1402 #, python-brace-format @@ -315,12 +316,12 @@ msgstr "Esperava uma lista de itens, mas recebeu tipo \"{input_type}\"." #: fields.py:1458 msgid "This selection may not be empty." -msgstr "Esta seleção não pode estar vazia." +msgstr "Esta seleção pode não estar vazia." #: fields.py:1495 #, python-brace-format msgid "\"{input}\" is not a valid path choice." -msgstr "\"{input}\" não é uma escolha válida para um caminho." +msgstr "\"{input}\" não é uma escolha válida de caminho." #: fields.py:1514 msgid "No file was submitted." @@ -329,7 +330,7 @@ msgstr "Nenhum arquivo foi submetido." #: fields.py:1515 msgid "" "The submitted data was not a file. Check the encoding type on the form." -msgstr "O dado submetido não é um arquivo. Certifique-se do tipo de codificação no formulário." +msgstr "O dado submetido não era um arquivo. Cheque o tipo de codificação no formulário." #: fields.py:1516 msgid "No filename could be determined." @@ -343,7 +344,7 @@ msgstr "O arquivo submetido está vázio." #, python-brace-format msgid "" "Ensure this filename has at most {max_length} characters (it has {length})." -msgstr "Certifique-se de que o nome do arquivo tem menos de {max_length} caracteres (tem {length})." +msgstr "Certifique-se de que o nome do arquivo tenho no máximo {max_length} caracteres (tem {length})." #: fields.py:1566 msgid "" @@ -353,7 +354,7 @@ msgstr "Faça upload de uma imagem válida. O arquivo enviado não é um arquivo #: fields.py:1604 relations.py:486 serializers.py:571 msgid "This list may not be empty." -msgstr "Esta lista não pode estar vazia." +msgstr "Esta lista pode não estar vazia." #: fields.py:1605 #, python-brace-format @@ -368,11 +369,11 @@ msgstr "Certifique-se de que este campo não tenha mais que {max_length} element #: fields.py:1682 #, python-brace-format msgid "Expected a dictionary of items but got type \"{input_type}\"." -msgstr "Esperava um dicionário de itens mas recebeu tipo \"{input_type}\"." +msgstr "Esperava um dicionário de itens, mas recebeu tipo \"{input_type}\"." #: fields.py:1683 msgid "This dictionary may not be empty." -msgstr "Este dicionário não pode estar vazio." +msgstr "Este dicionário pode não estar vazio." #: fields.py:1755 msgid "Value must be valid JSON." @@ -451,7 +452,7 @@ msgstr "Hyperlink inválido - Objeto não existe." #: relations.py:283 #, python-brace-format msgid "Incorrect type. Expected URL string, received {data_type}." -msgstr "Tipo incorreto. Necessário string URL, recebeu {data_type}." +msgstr "Tipo incorreto. Esperava string URL, recebeu {data_type}." #: relations.py:448 #, python-brace-format @@ -482,7 +483,7 @@ msgstr "Um {value_type} que identifica este {name}." #: serializers.py:337 #, python-brace-format msgid "Invalid data. Expected a dictionary, but got {datatype}." -msgstr "Dado inválido. Necessário um dicionário mas recebeu {datatype}." +msgstr "Dado inválido. Esperava um dicionário, mas recebeu {datatype}." #: templates/rest_framework/admin.html:116 #: templates/rest_framework/base.html:136 @@ -500,23 +501,23 @@ msgstr "" #: templates/rest_framework/base.html:75 msgid "content" -msgstr "" +msgstr "conteúdo" #: templates/rest_framework/base.html:78 msgid "request form" -msgstr "" +msgstr "formulário de requisição" #: templates/rest_framework/base.html:157 msgid "main content" -msgstr "" +msgstr "conteúdo principal" #: templates/rest_framework/base.html:173 msgid "request info" -msgstr "" +msgstr "informações da requisição" #: templates/rest_framework/base.html:177 msgid "response info" -msgstr "" +msgstr "informações da resposta" #: templates/rest_framework/horizontal/radio.html:4 #: templates/rest_framework/inline/radio.html:3 @@ -528,11 +529,11 @@ msgstr "Nenhum(a/as)" #: templates/rest_framework/inline/select_multiple.html:3 #: templates/rest_framework/vertical/select_multiple.html:3 msgid "No items to select." -msgstr "Nenhum item para escholher." +msgstr "Nenhum item para selecionar." #: validators.py:39 msgid "This field must be unique." -msgstr "Esse campo deve ser único." +msgstr "Este campo deve ser único." #: validators.py:89 #, python-brace-format @@ -547,17 +548,17 @@ msgstr "Caracteres substitutos não são permitidos: U+{code_point:X}." #: validators.py:243 #, python-brace-format msgid "This field must be unique for the \"{date_field}\" date." -msgstr "O campo \"{date_field}\" deve ser único para a data." +msgstr "Este campo deve ser único para a data de \"{date_field}\"." #: validators.py:258 #, python-brace-format msgid "This field must be unique for the \"{date_field}\" month." -msgstr "O campo \"{date_field}\" deve ser único para o mês." +msgstr "Este campo deve ser único para o mês de \"{date_field}\"." #: validators.py:271 #, python-brace-format msgid "This field must be unique for the \"{date_field}\" year." -msgstr "O campo \"{date_field}\" deve ser único para o ano." +msgstr "Este campo deve ser único para o ano de \"{date_field}\"." #: versioning.py:40 msgid "Invalid version in \"Accept\" header." @@ -577,4 +578,4 @@ msgstr "Versão inválida no hostname." #: versioning.py:170 msgid "Invalid version in query parameter." -msgstr "Versão inválida no parâmetro de query." +msgstr "Versão inválida no parâmetro de consulta." From 40172399afcc60510a78bdae39818ee6686b72e4 Mon Sep 17 00:00:00 2001 From: Rodrigo Date: Fri, 5 Dec 2025 09:01:25 -0300 Subject: [PATCH 13/13] Fix Documentation (#9832) --- docs/api-guide/serializers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index 8d4da4ee4..e94b943a2 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -708,7 +708,7 @@ You can override a URL field view name and lookup field by using either, or both class AccountSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Account - fields = ['account_url', 'account_name', 'users', 'created'] + fields = ['url', 'account_name', 'users', 'created'] extra_kwargs = { 'url': {'view_name': 'accounts', 'lookup_field': 'account_name'}, 'users': {'lookup_field': 'username'}