mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-12-15 06:04:00 +03:00
Merge branch 'main' into patch-1
This commit is contained in:
commit
d94f427f5e
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
|
|
@ -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:
|
||||
|
|
|
|||
2
.github/workflows/mkdocs-deploy.yml
vendored
2
.github/workflows/mkdocs-deploy.yml
vendored
|
|
@ -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:
|
||||
|
|
|
|||
2
.github/workflows/pre-commit.yml
vendored
2
.github/workflows/pre-commit.yml
vendored
|
|
@ -11,7 +11,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
48
README.md
48
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.
|
||||
|
|
@ -55,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.
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -3,4 +3,5 @@ assertIn
|
|||
IAM
|
||||
endcode
|
||||
deque
|
||||
thead
|
||||
thead
|
||||
lets
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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**:
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,388 +0,0 @@
|
|||
<script>
|
||||
// Imperfect, but easier to fit in with the existing docs build.
|
||||
// Hyperlinks should point directly to the "fund." subdomain, but this'll
|
||||
// handle the nav bar links without requiring any docs build changes for the moment.
|
||||
if (window.location.hostname == "www.django-rest-framework.org") {
|
||||
window.location.replace("https://fund.django-rest-framework.org/topics/funding/");
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.promo li a {
|
||||
float: left;
|
||||
width: 130px;
|
||||
height: 20px;
|
||||
text-align: center;
|
||||
margin: 10px 30px;
|
||||
padding: 150px 0 0 0;
|
||||
background-position: 0 50%;
|
||||
background-size: 130px auto;
|
||||
background-repeat: no-repeat;
|
||||
font-size: 120%;
|
||||
color: black;
|
||||
}
|
||||
.promo li {
|
||||
list-style: none;
|
||||
}
|
||||
.chart {
|
||||
background-color: #e3e3e3;
|
||||
background: -webkit-linear-gradient(top, #fff 0, #e3e3e3 100%);
|
||||
border: 1px solid #E6E6E6;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0px 0px 2px 0px rgba(181, 181, 181, 0.3);
|
||||
padding: 40px 0px 5px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
width: 97%;
|
||||
min-height: 255px;
|
||||
position: relative;
|
||||
top: 37px;
|
||||
margin-bottom: 20px
|
||||
}
|
||||
.quantity {
|
||||
text-align: center
|
||||
}
|
||||
.dollar {
|
||||
font-size: 19px;
|
||||
position: relative;
|
||||
top: -18px;
|
||||
}
|
||||
.price {
|
||||
font-size: 49px;
|
||||
}
|
||||
.period {
|
||||
font-size: 17px;
|
||||
position: relative;
|
||||
top: -8px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
.plan-name {
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
font-weight: 400;
|
||||
color: #777;
|
||||
border-bottom: 1px solid #d5d5d5;
|
||||
padding-bottom: 15px;
|
||||
width: 90%;
|
||||
margin: 0 auto;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.specs {
|
||||
margin-top: 20px;
|
||||
min-height: 130px;
|
||||
}
|
||||
.specs.freelancer {
|
||||
min-height: 0px;
|
||||
}
|
||||
.spec {
|
||||
font-size: 15px;
|
||||
color: #474747;
|
||||
text-align: center;
|
||||
font-weight: 300;
|
||||
margin-bottom: 13px;
|
||||
}
|
||||
.variable {
|
||||
color: #1FBEE7;
|
||||
font-weight: 400;
|
||||
}
|
||||
form.signup {
|
||||
margin-top: 35px
|
||||
}
|
||||
.clear-promo {
|
||||
padding-top: 30px
|
||||
}
|
||||
#main-content h1:first-of-type {
|
||||
margin: 0 0 50px;
|
||||
font-size: 60px;
|
||||
font-weight: 200;
|
||||
text-align: center
|
||||
}
|
||||
#main-content {
|
||||
padding-top: 10px; line-height: 23px
|
||||
}
|
||||
#main-content li {
|
||||
line-height: 23px
|
||||
}
|
||||
</style>
|
||||
|
||||
# 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).
|
||||
|
||||
<div class="pricing">
|
||||
<div class="span4">
|
||||
<div class="chart first">
|
||||
<div class="quantity">
|
||||
<span class="dollar">{{ symbol }}</span>
|
||||
<span class="price">{{ rates.personal1 }}</span>
|
||||
<span class="period">/month{% if vat %} +VAT{% endif %}</span>
|
||||
</div>
|
||||
<div class="plan-name">Individual</div>
|
||||
<div class="specs freelancer">
|
||||
<div class="spec">
|
||||
Support ongoing development
|
||||
</div>
|
||||
<div class="spec">
|
||||
Credited on the site
|
||||
</div>
|
||||
</div>
|
||||
<form class="signup" action="/signup/{{ currency }}-{{ rates.personal1 }}/" method="POST">
|
||||
<script
|
||||
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
|
||||
data-key="{{ stripe_public }}"
|
||||
data-amount="{{ stripe_amounts.personal1 }}"
|
||||
data-name="Django REST framework"
|
||||
data-description="Individual"
|
||||
data-currency="{{ currency }}"
|
||||
data-allow-remember-me=false
|
||||
data-billing-address=true
|
||||
data-label='Sign up'
|
||||
data-panel-label='Sign up - {% verbatim %}{{amount}}{% endverbatim %}/mo'>
|
||||
</script>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="clear: both; padding-top: 50px"></div>
|
||||
|
||||
*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.
|
||||
|
||||
<div class="pricing">
|
||||
<div class="span4">
|
||||
<div class="chart first">
|
||||
<div class="quantity">
|
||||
<span class="dollar">{{ symbol }}</span>
|
||||
<span class="price">{{ rates.corporate1 }}</span>
|
||||
<span class="period">/month{% if vat %} +VAT{% endif %}</span>
|
||||
</div>
|
||||
<div class="plan-name">Basic</div>
|
||||
<div class="specs startup">
|
||||
<div class="spec">
|
||||
Support ongoing development
|
||||
</div>
|
||||
<div class="spec">
|
||||
<span class="variable">Funding page</span> ad placement
|
||||
</div>
|
||||
</div>
|
||||
<form class="signup" action="/signup/{{ currency }}-{{ rates.corporate1 }}/" method="POST">
|
||||
<script
|
||||
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
|
||||
data-key="{{ stripe_public }}"
|
||||
data-amount="{{ stripe_amounts.corporate1 }}"
|
||||
data-name="Django REST framework"
|
||||
data-description="Basic"
|
||||
data-currency="{{ currency }}"
|
||||
data-allow-remember-me=false
|
||||
data-billing-address=true
|
||||
data-label='Sign up'
|
||||
data-panel-label='Sign up - {% verbatim %}{{amount}}{% endverbatim %}/mo'>
|
||||
</script>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="span4">
|
||||
<div class="chart">
|
||||
<div class="quantity">
|
||||
<span class="dollar">{{ symbol }}</span>
|
||||
<span class="price">{{ rates.corporate2 }}</span>
|
||||
<span class="period">/month{% if vat %} +VAT{% endif %}</span>
|
||||
</div>
|
||||
<div class="plan-name">Professional</div>
|
||||
<div class="specs">
|
||||
<div class="spec">
|
||||
Support ongoing development
|
||||
</div>
|
||||
<div class="spec">
|
||||
<span class="variable">Sidebar</span> ad placement
|
||||
</div>
|
||||
<div class="spec">
|
||||
<span class="variable">Priority support</span> for your engineers
|
||||
</div>
|
||||
</div>
|
||||
<form class="signup" action="/signup/{{ currency }}-{{ rates.corporate2 }}/" method="POST">
|
||||
<script
|
||||
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
|
||||
data-key="{{ stripe_public }}"
|
||||
data-amount="{{ stripe_amounts.corporate2 }}"
|
||||
data-name="Django REST framework"
|
||||
data-description="Professional"
|
||||
data-currency="{{ currency }}"
|
||||
data-allow-remember-me=false
|
||||
data-billing-address=true
|
||||
data-label='Sign up'
|
||||
data-panel-label='Sign up - {% verbatim %}{{amount}}{% endverbatim %}/mo'>
|
||||
</script>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="span4">
|
||||
<div class="chart last">
|
||||
<div class="quantity">
|
||||
<span class="dollar">{{ symbol }}</span>
|
||||
<span class="price">{{ rates.corporate3 }}</span>
|
||||
<span class="period">/month{% if vat %} +VAT{% endif %}</span>
|
||||
</div>
|
||||
<div class="plan-name">Premium</div>
|
||||
<div class="specs">
|
||||
<div class="spec">
|
||||
Support ongoing development
|
||||
</div>
|
||||
<div class="spec">
|
||||
<span class="variable">Homepage</span> ad placement
|
||||
</div>
|
||||
<div class="spec">
|
||||
<span class="variable">Sidebar</span> ad placement
|
||||
</div>
|
||||
<div class="spec">
|
||||
<span class="variable">Priority support</span> for your engineers
|
||||
</div>
|
||||
</div>
|
||||
<form class="signup" action="/signup/{{ currency }}-{{ rates.corporate3 }}/" method="POST">
|
||||
<script
|
||||
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
|
||||
data-key="{{ stripe_public }}"
|
||||
data-amount="{{ stripe_amounts.corporate3 }}"
|
||||
data-name="Django REST framework"
|
||||
data-description="Premium"
|
||||
data-currency="{{ currency }}"
|
||||
data-allow-remember-me=false
|
||||
data-billing-address=true
|
||||
data-label='Sign up'
|
||||
data-panel-label='Sign up - {% verbatim %}{{amount}}{% endverbatim %}/mo'>
|
||||
</script>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="clear: both; padding-top: 50px"></div>
|
||||
|
||||
*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 <a href=mailto:funding@django-rest-framework.org>funding@django-rest-framework.org</a>.
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
|
||||
<!-- Begin MailChimp Signup Form -->
|
||||
<link href="//cdn-images.mailchimp.com/embedcode/classic-10_7.css" rel="stylesheet" type="text/css">
|
||||
<style type="text/css">
|
||||
#mc_embed_signup{background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; }
|
||||
/* Add your own MailChimp form style overrides in your site stylesheet or in this style block.
|
||||
We recommend moving this block and the preceding CSS link to the HEAD of your HTML file. */
|
||||
</style>
|
||||
<div id="mc_embed_signup">
|
||||
<form action="//encode.us13.list-manage.com/subscribe/post?u=b6b66bb5e4c7cb484a85c8dd7&id=e382ef68ef" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
|
||||
<div id="mc_embed_signup_scroll">
|
||||
<h2>Stay up to date, with our monthly progress reports...</h2>
|
||||
<div class="mc-field-group">
|
||||
<label for="mce-EMAIL">Email Address </label>
|
||||
<input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL">
|
||||
</div>
|
||||
<div id="mce-responses" class="clear">
|
||||
<div class="response" id="mce-error-response" style="display:none"></div>
|
||||
<div class="response" id="mce-success-response" style="display:none"></div>
|
||||
</div> <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->
|
||||
<div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_b6b66bb5e4c7cb484a85c8dd7_e382ef68ef" tabindex="-1" value=""></div>
|
||||
<div class="clear"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button"></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<script type='text/javascript' src='//s3.amazonaws.com/downloads.mailchimp.com/js/mc-validate.js'></script><script type='text/javascript'>(function($) {window.fnames = new Array(); window.ftypes = new Array();fnames[0]='EMAIL';ftypes[0]='email';fnames[1]='FNAME';ftypes[1]='text';fnames[2]='LNAME';ftypes[2]='text';}(jQuery));var $mcj = jQuery.noConflict(true);</script>
|
||||
<!--End mc_embed_signup-->
|
||||
|
||||
---
|
||||
|
||||
## Frequently asked questions
|
||||
|
||||
**Q: Can you issue monthly invoices?**
|
||||
A: Yes, we are happy to issue monthly invoices. Please just <a href=mailto:funding@django-rest-framework.org>email us</a> 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
|
||||
|
||||
<div id="fundingInclude"></div>
|
||||
|
||||
<script src="https://fund.django-rest-framework.org/funding_include.js"></script>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.*
|
||||
|
||||
<ul class="premium-promo promo">
|
||||
<li><a href="https://getsentry.com/welcome/" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/sentry130.png)">Sentry</a></li>
|
||||
<li><a href="https://getstream.io/?utm_source=DjangoRESTFramework&utm_medium=Webpage_Logo_Ad&utm_content=Developer&utm_campaign=DjangoRESTFramework_Jan2022_HomePage" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/stream-130.png)">Stream</a></li>
|
||||
<li><a href="https://www.spacinov.com/" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/spacinov.png)">Spacinov</a></li>
|
||||
<li><a href="https://retool.com/?utm_source=djangorest&utm_medium=sponsorship" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/retool-sidebar.png)">Retool</a></li>
|
||||
<li><a href="https://bit.io/jobs?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/bitio_logo_gold_background.png)">bit.io</a></li>
|
||||
<li><a href="https://posthog.com?utm_source=DRF&utm_medium=sponsor&utm_campaign=DRF_sponsorship" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/135996800-d49fe024-32d9-441a-98d9-4c7596287a67.png)">PostHog</a></li>
|
||||
<li><a href="https://cryptapi.io" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/cryptapi.png)">CryptAPI</a></li>
|
||||
<li><a href="https://www.fezto.xyz/?utm_source=DjangoRESTFramework" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/fezto.png)">FEZTO</a></li>
|
||||
<li><a href="https://www.svix.com/?utm_source=django-REST&utm_medium=sponsorship" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/svix.png)">Svix</a></li>
|
||||
<li><a href="https://zuplo.link/django-web" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/zuplo.png)">Zuplo</a></li>
|
||||
</ul>
|
||||
<div style="clear: both; padding-bottom: 20px;"></div>
|
||||
|
||||
*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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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 = [
|
||||
|
|
@ -17,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",
|
||||
|
|
@ -44,6 +46,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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.'),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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']
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -171,7 +173,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=\[<django.core.validators.RegexValidator object>\]\)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
11
tox.ini
11
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
|
||||
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
|
||||
django<6.1
|
||||
-rrequirements/requirements-testing.txt
|
||||
-rrequirements/requirements-optionals.txt
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user