Don't use Windows line endings

This commit is contained in:
Peter Thomassen 2024-04-26 18:05:13 +02:00
parent b34bde47d7
commit 9d4ed054bf
No known key found for this signature in database
GPG Key ID: 5203651ED2F34D28
3 changed files with 773 additions and 773 deletions

View File

@ -1,398 +1,398 @@
<script> <script>
// Imperfect, but easier to fit in with the existing docs build. // Imperfect, but easier to fit in with the existing docs build.
// Hyperlinks should point directly to the "fund." subdomain, but this'll // 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. // handle the nav bar links without requiring any docs build changes for the moment.
if (window.location.hostname == "www.django-rest-framework.org") { if (window.location.hostname == "www.django-rest-framework.org") {
window.location.replace("https://fund.django-rest-framework.org/topics/funding/"); window.location.replace("https://fund.django-rest-framework.org/topics/funding/");
} }
</script> </script>
<style> <style>
.promo li a { .promo li a {
float: left; float: left;
width: 130px; width: 130px;
height: 20px; height: 20px;
text-align: center; text-align: center;
margin: 10px 30px; margin: 10px 30px;
padding: 150px 0 0 0; padding: 150px 0 0 0;
background-position: 0 50%; background-position: 0 50%;
background-size: 130px auto; background-size: 130px auto;
background-repeat: no-repeat; background-repeat: no-repeat;
font-size: 120%; font-size: 120%;
color: black; color: black;
} }
.promo li { .promo li {
list-style: none; list-style: none;
} }
.chart { .chart {
background-color: #e3e3e3; background-color: #e3e3e3;
background: -webkit-linear-gradient(top, #fff 0, #e3e3e3 100%); background: -webkit-linear-gradient(top, #fff 0, #e3e3e3 100%);
border: 1px solid #E6E6E6; border: 1px solid #E6E6E6;
border-radius: 5px; border-radius: 5px;
box-shadow: 0px 0px 2px 0px rgba(181, 181, 181, 0.3); box-shadow: 0px 0px 2px 0px rgba(181, 181, 181, 0.3);
padding: 40px 0px 5px; padding: 40px 0px 5px;
position: relative; position: relative;
text-align: center; text-align: center;
width: 97%; width: 97%;
min-height: 255px; min-height: 255px;
position: relative; position: relative;
top: 37px; top: 37px;
margin-bottom: 20px margin-bottom: 20px
} }
.quantity { .quantity {
text-align: center text-align: center
} }
.dollar { .dollar {
font-size: 19px; font-size: 19px;
position: relative; position: relative;
top: -18px; top: -18px;
} }
.price { .price {
font-size: 49px; font-size: 49px;
} }
.period { .period {
font-size: 17px; font-size: 17px;
position: relative; position: relative;
top: -8px; top: -8px;
margin-left: 4px; margin-left: 4px;
} }
.plan-name { .plan-name {
text-align: center; text-align: center;
font-size: 20px; font-size: 20px;
font-weight: 400; font-weight: 400;
color: #777; color: #777;
border-bottom: 1px solid #d5d5d5; border-bottom: 1px solid #d5d5d5;
padding-bottom: 15px; padding-bottom: 15px;
width: 90%; width: 90%;
margin: 0 auto; margin: 0 auto;
margin-top: 8px; margin-top: 8px;
} }
.specs { .specs {
margin-top: 20px; margin-top: 20px;
min-height: 130px; min-height: 130px;
} }
.specs.freelancer { .specs.freelancer {
min-height: 0px; min-height: 0px;
} }
.spec { .spec {
font-size: 15px; font-size: 15px;
color: #474747; color: #474747;
text-align: center; text-align: center;
font-weight: 300; font-weight: 300;
margin-bottom: 13px; margin-bottom: 13px;
} }
.variable { .variable {
color: #1FBEE7; color: #1FBEE7;
font-weight: 400; font-weight: 400;
} }
form.signup { form.signup {
margin-top: 35px margin-top: 35px
} }
.clear-promo { .clear-promo {
padding-top: 30px padding-top: 30px
} }
#main-content h1:first-of-type { #main-content h1:first-of-type {
margin: 0 0 50px; margin: 0 0 50px;
font-size: 60px; font-size: 60px;
font-weight: 200; font-weight: 200;
text-align: center text-align: center
} }
#main-content { #main-content {
padding-top: 10px; line-height: 23px padding-top: 10px; line-height: 23px
} }
#main-content li { #main-content li {
line-height: 23px line-height: 23px
} }
</style> </style>
# Funding # Funding
If you use REST framework commercially we strongly encourage you to invest in its continued development by signing up for a paid plan. 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.** **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: Signing up for a paid plan will:
* Directly contribute to faster releases, more features, and higher quality software. * Directly contribute to faster releases, more features, and higher quality software.
* Allow more time to be invested in documentation, issue triage, and community support. * Allow more time to be invested in documentation, issue triage, and community support.
* Safeguard the future development of REST framework. * 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. 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 ## 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.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.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 [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/). * 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. * 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. * 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. * 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. * Contracting development time for the work on the JavaScript client library and API documentation tooling.
--- ---
## What future funding will enable ## What future funding will enable
* Realtime API support, using WebSockets. This will consist of documentation and support for using REST framework together with Django Channels, plus integrating WebSocket support into the client libraries. * Realtime API support, using WebSockets. This will consist of documentation and support for using REST framework together with Django Channels, plus integrating WebSocket support into the client libraries.
* Better authentication defaults, possibly bringing JWT & CORS support into the core package. * Better authentication defaults, possibly bringing JWT & CORS support into the core package.
* Securing the community & operations manager position long-term. * Securing the community & operations manager position long-term.
* Opening up and securing a part-time position to focus on ticket triage and resolution. * Opening up and securing a part-time position to focus on ticket triage and resolution.
* Paying for development time on building API client libraries in a range of programming languages. These would be integrated directly into the upcoming API documentation. * Paying for development time on building API client libraries in a range of programming languages. These would be integrated directly into the upcoming API documentation.
Sign up for a paid plan today, and help ensure that REST framework becomes a sustainable, full-time funded project. Sign up for a paid plan today, and help ensure that REST framework becomes a sustainable, full-time funded project.
--- ---
## What our sponsors and users say ## 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. > 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.
> >
> &mdash; José Padilla, Django REST framework contributor > &mdash; José Padilla, Django REST framework contributor
&nbsp; &nbsp;
> 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. > 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.
> >
> &mdash; Filipe Ximenes, Vinta Software > &mdash; Filipe Ximenes, Vinta Software
&nbsp; &nbsp;
> 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. > 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. 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.
> >
> &mdash; Andrew Conti, Django REST framework user > &mdash; Andrew Conti, Django REST framework user
--- ---
## Individual plan ## Individual plan
This subscription is recommended for individuals with an interest in seeing REST framework continue to&nbsp;improve. This subscription is recommended for individuals with an interest in seeing REST framework continue to&nbsp;improve.
If you are using REST framework as a full-time employee, consider recommending that your company takes out a [corporate&nbsp;plan](#corporate-plans). If you are using REST framework as a full-time employee, consider recommending that your company takes out a [corporate&nbsp;plan](#corporate-plans).
<div class="pricing"> <div class="pricing">
<div class="span4"> <div class="span4">
<div class="chart first"> <div class="chart first">
<div class="quantity"> <div class="quantity">
<span class="dollar">{{ symbol }}</span> <span class="dollar">{{ symbol }}</span>
<span class="price">{{ rates.personal1 }}</span> <span class="price">{{ rates.personal1 }}</span>
<span class="period">/month{% if vat %} +VAT{% endif %}</span> <span class="period">/month{% if vat %} +VAT{% endif %}</span>
</div> </div>
<div class="plan-name">Individual</div> <div class="plan-name">Individual</div>
<div class="specs freelancer"> <div class="specs freelancer">
<div class="spec"> <div class="spec">
Support ongoing development Support ongoing development
</div> </div>
<div class="spec"> <div class="spec">
Credited on the site Credited on the site
</div> </div>
</div> </div>
<form class="signup" action="/signup/{{ currency }}-{{ rates.personal1 }}/" method="POST"> <form class="signup" action="/signup/{{ currency }}-{{ rates.personal1 }}/" method="POST">
<script <script
src="https://checkout.stripe.com/checkout.js" class="stripe-button" src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="{{ stripe_public }}" data-key="{{ stripe_public }}"
data-amount="{{ stripe_amounts.personal1 }}" data-amount="{{ stripe_amounts.personal1 }}"
data-name="Django REST framework" data-name="Django REST framework"
data-description="Individual" data-description="Individual"
data-currency="{{ currency }}" data-currency="{{ currency }}"
data-allow-remember-me=false data-allow-remember-me=false
data-billing-address=true data-billing-address=true
data-label='Sign up' data-label='Sign up'
data-panel-label='Sign up - {% verbatim %}{{amount}}{% endverbatim %}/mo'> data-panel-label='Sign up - {% verbatim %}{{amount}}{% endverbatim %}/mo'>
</script> </script>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
<div style="clear: both; padding-top: 50px"></div> <div style="clear: both; padding-top: 50px"></div>
*Billing is monthly and you can cancel at any time.* *Billing is monthly and you can cancel at any time.*
--- ---
## Corporate plans ## Corporate plans
These subscriptions are recommended for companies and organizations using REST framework either publicly or privately. 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**. 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. 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="pricing">
<div class="span4"> <div class="span4">
<div class="chart first"> <div class="chart first">
<div class="quantity"> <div class="quantity">
<span class="dollar">{{ symbol }}</span> <span class="dollar">{{ symbol }}</span>
<span class="price">{{ rates.corporate1 }}</span> <span class="price">{{ rates.corporate1 }}</span>
<span class="period">/month{% if vat %} +VAT{% endif %}</span> <span class="period">/month{% if vat %} +VAT{% endif %}</span>
</div> </div>
<div class="plan-name">Basic</div> <div class="plan-name">Basic</div>
<div class="specs startup"> <div class="specs startup">
<div class="spec"> <div class="spec">
Support ongoing development Support ongoing development
</div> </div>
<div class="spec"> <div class="spec">
<span class="variable">Funding page</span> ad placement <span class="variable">Funding page</span> ad placement
</div> </div>
</div> </div>
<form class="signup" action="/signup/{{ currency }}-{{ rates.corporate1 }}/" method="POST"> <form class="signup" action="/signup/{{ currency }}-{{ rates.corporate1 }}/" method="POST">
<script <script
src="https://checkout.stripe.com/checkout.js" class="stripe-button" src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="{{ stripe_public }}" data-key="{{ stripe_public }}"
data-amount="{{ stripe_amounts.corporate1 }}" data-amount="{{ stripe_amounts.corporate1 }}"
data-name="Django REST framework" data-name="Django REST framework"
data-description="Basic" data-description="Basic"
data-currency="{{ currency }}" data-currency="{{ currency }}"
data-allow-remember-me=false data-allow-remember-me=false
data-billing-address=true data-billing-address=true
data-label='Sign up' data-label='Sign up'
data-panel-label='Sign up - {% verbatim %}{{amount}}{% endverbatim %}/mo'> data-panel-label='Sign up - {% verbatim %}{{amount}}{% endverbatim %}/mo'>
</script> </script>
</form> </form>
</div> </div>
</div> </div>
<div class="span4"> <div class="span4">
<div class="chart"> <div class="chart">
<div class="quantity"> <div class="quantity">
<span class="dollar">{{ symbol }}</span> <span class="dollar">{{ symbol }}</span>
<span class="price">{{ rates.corporate2 }}</span> <span class="price">{{ rates.corporate2 }}</span>
<span class="period">/month{% if vat %} +VAT{% endif %}</span> <span class="period">/month{% if vat %} +VAT{% endif %}</span>
</div> </div>
<div class="plan-name">Professional</div> <div class="plan-name">Professional</div>
<div class="specs"> <div class="specs">
<div class="spec"> <div class="spec">
Support ongoing development Support ongoing development
</div> </div>
<div class="spec"> <div class="spec">
<span class="variable">Sidebar</span> ad placement <span class="variable">Sidebar</span> ad placement
</div> </div>
<div class="spec"> <div class="spec">
<span class="variable">Priority support</span> for your engineers <span class="variable">Priority support</span> for your engineers
</div> </div>
</div> </div>
<form class="signup" action="/signup/{{ currency }}-{{ rates.corporate2 }}/" method="POST"> <form class="signup" action="/signup/{{ currency }}-{{ rates.corporate2 }}/" method="POST">
<script <script
src="https://checkout.stripe.com/checkout.js" class="stripe-button" src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="{{ stripe_public }}" data-key="{{ stripe_public }}"
data-amount="{{ stripe_amounts.corporate2 }}" data-amount="{{ stripe_amounts.corporate2 }}"
data-name="Django REST framework" data-name="Django REST framework"
data-description="Professional" data-description="Professional"
data-currency="{{ currency }}" data-currency="{{ currency }}"
data-allow-remember-me=false data-allow-remember-me=false
data-billing-address=true data-billing-address=true
data-label='Sign up' data-label='Sign up'
data-panel-label='Sign up - {% verbatim %}{{amount}}{% endverbatim %}/mo'> data-panel-label='Sign up - {% verbatim %}{{amount}}{% endverbatim %}/mo'>
</script> </script>
</form> </form>
</div> </div>
</div> </div>
<div class="span4"> <div class="span4">
<div class="chart last"> <div class="chart last">
<div class="quantity"> <div class="quantity">
<span class="dollar">{{ symbol }}</span> <span class="dollar">{{ symbol }}</span>
<span class="price">{{ rates.corporate3 }}</span> <span class="price">{{ rates.corporate3 }}</span>
<span class="period">/month{% if vat %} +VAT{% endif %}</span> <span class="period">/month{% if vat %} +VAT{% endif %}</span>
</div> </div>
<div class="plan-name">Premium</div> <div class="plan-name">Premium</div>
<div class="specs"> <div class="specs">
<div class="spec"> <div class="spec">
Support ongoing development Support ongoing development
</div> </div>
<div class="spec"> <div class="spec">
<span class="variable">Homepage</span> ad placement <span class="variable">Homepage</span> ad placement
</div> </div>
<div class="spec"> <div class="spec">
<span class="variable">Sidebar</span> ad placement <span class="variable">Sidebar</span> ad placement
</div> </div>
<div class="spec"> <div class="spec">
<span class="variable">Priority support</span> for your engineers <span class="variable">Priority support</span> for your engineers
</div> </div>
</div> </div>
<form class="signup" action="/signup/{{ currency }}-{{ rates.corporate3 }}/" method="POST"> <form class="signup" action="/signup/{{ currency }}-{{ rates.corporate3 }}/" method="POST">
<script <script
src="https://checkout.stripe.com/checkout.js" class="stripe-button" src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="{{ stripe_public }}" data-key="{{ stripe_public }}"
data-amount="{{ stripe_amounts.corporate3 }}" data-amount="{{ stripe_amounts.corporate3 }}"
data-name="Django REST framework" data-name="Django REST framework"
data-description="Premium" data-description="Premium"
data-currency="{{ currency }}" data-currency="{{ currency }}"
data-allow-remember-me=false data-allow-remember-me=false
data-billing-address=true data-billing-address=true
data-label='Sign up' data-label='Sign up'
data-panel-label='Sign up - {% verbatim %}{{amount}}{% endverbatim %}/mo'> data-panel-label='Sign up - {% verbatim %}{{amount}}{% endverbatim %}/mo'>
</script> </script>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
<div style="clear: both; padding-top: 50px"></div> <div style="clear: both; padding-top: 50px"></div>
*Billing is monthly and you can cancel at any time.* *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. 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>. For further enquires please contact <a href=mailto:funding@django-rest-framework.org>funding@django-rest-framework.org</a>.
--- ---
## Accountability ## 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. 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 --> <!-- Begin MailChimp Signup Form -->
<link href="//cdn-images.mailchimp.com/embedcode/classic-10_7.css" rel="stylesheet" type="text/css"> <link href="//cdn-images.mailchimp.com/embedcode/classic-10_7.css" rel="stylesheet" type="text/css">
<style type="text/css"> <style type="text/css">
#mc_embed_signup{background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; } #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. /* 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. */ We recommend moving this block and the preceding CSS link to the HEAD of your HTML file. */
</style> </style>
<div id="mc_embed_signup"> <div id="mc_embed_signup">
<form action="//encode.us13.list-manage.com/subscribe/post?u=b6b66bb5e4c7cb484a85c8dd7&amp;id=e382ef68ef" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate> <form action="//encode.us13.list-manage.com/subscribe/post?u=b6b66bb5e4c7cb484a85c8dd7&amp;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"> <div id="mc_embed_signup_scroll">
<h2>Stay up to date, with our monthly progress reports...</h2> <h2>Stay up to date, with our monthly progress reports...</h2>
<div class="mc-field-group"> <div class="mc-field-group">
<label for="mce-EMAIL">Email Address </label> <label for="mce-EMAIL">Email Address </label>
<input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL"> <input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL">
</div> </div>
<div id="mce-responses" class="clear"> <div id="mce-responses" class="clear">
<div class="response" id="mce-error-response" style="display:none"></div> <div class="response" id="mce-error-response" style="display:none"></div>
<div class="response" id="mce-success-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> <!-- 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 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 class="clear"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button"></div>
</div> </div>
</form> </form>
</div> </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> <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--> <!--End mc_embed_signup-->
--- ---
## Frequently asked questions ## Frequently asked questions
**Q: Can you issue monthly invoices?** **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. 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?** **Q: Does sponsorship include VAT?**
A: Sponsorship is VAT exempt. A: Sponsorship is VAT exempt.
**Q: Do I have to sign up for a certain time period?** **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. 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?** **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. 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?** **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. A: No, we value individual sponsors just as much as corporate sponsors and appreciate any kind of support.
--- ---
## Our sponsors ## Our sponsors
<div id="fundingInclude"></div> <div id="fundingInclude"></div>
<script src="https://fund.django-rest-framework.org/funding_include.js"></script> <script src="https://fund.django-rest-framework.org/funding_include.js"></script>

View File

@ -1,220 +1,220 @@
# HTML & Forms # HTML & Forms
REST framework is suitable for returning both API style responses, and regular HTML pages. Additionally, serializers can be used as HTML forms and rendered in templates. REST framework is suitable for returning both API style responses, and regular HTML pages. Additionally, serializers can be used as HTML forms and rendered in templates.
## Rendering HTML ## Rendering HTML
In order to return HTML responses you'll need to use either `TemplateHTMLRenderer`, or `StaticHTMLRenderer`. In order to return HTML responses you'll need to use either `TemplateHTMLRenderer`, or `StaticHTMLRenderer`.
The `TemplateHTMLRenderer` class expects the response to contain a dictionary of context data, and renders an HTML page based on a template that must be specified either in the view or on the response. The `TemplateHTMLRenderer` class expects the response to contain a dictionary of context data, and renders an HTML page based on a template that must be specified either in the view or on the response.
The `StaticHTMLRender` class expects the response to contain a string of the pre-rendered HTML content. The `StaticHTMLRender` class expects the response to contain a string of the pre-rendered HTML content.
Because static HTML pages typically have different behavior from API responses you'll probably need to write any HTML views explicitly, rather than relying on the built-in generic views. Because static HTML pages typically have different behavior from API responses you'll probably need to write any HTML views explicitly, rather than relying on the built-in generic views.
Here's an example of a view that returns a list of "Profile" instances, rendered in an HTML template: Here's an example of a view that returns a list of "Profile" instances, rendered in an HTML template:
**views.py**: **views.py**:
from my_project.example.models import Profile from my_project.example.models import Profile
from rest_framework.renderers import TemplateHTMLRenderer from rest_framework.renderers import TemplateHTMLRenderer
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
class ProfileList(APIView): class ProfileList(APIView):
renderer_classes = [TemplateHTMLRenderer] renderer_classes = [TemplateHTMLRenderer]
template_name = 'profile_list.html' template_name = 'profile_list.html'
def get(self, request): def get(self, request):
queryset = Profile.objects.all() queryset = Profile.objects.all()
return Response({'profiles': queryset}) return Response({'profiles': queryset})
**profile_list.html**: **profile_list.html**:
<html><body> <html><body>
<h1>Profiles</h1> <h1>Profiles</h1>
<ul> <ul>
{% for profile in profiles %} {% for profile in profiles %}
<li>{{ profile.name }}</li> <li>{{ profile.name }}</li>
{% endfor %} {% endfor %}
</ul> </ul>
</body></html> </body></html>
## Rendering Forms ## Rendering Forms
Serializers may be rendered as forms by using the `render_form` template tag, and including the serializer instance as context to the template. Serializers may be rendered as forms by using the `render_form` template tag, and including the serializer instance as context to the template.
The following view demonstrates an example of using a serializer in a template for viewing and updating a model instance: The following view demonstrates an example of using a serializer in a template for viewing and updating a model instance:
**views.py**: **views.py**:
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from my_project.example.models import Profile from my_project.example.models import Profile
from rest_framework.renderers import TemplateHTMLRenderer from rest_framework.renderers import TemplateHTMLRenderer
from rest_framework.views import APIView from rest_framework.views import APIView
class ProfileDetail(APIView): class ProfileDetail(APIView):
renderer_classes = [TemplateHTMLRenderer] renderer_classes = [TemplateHTMLRenderer]
template_name = 'profile_detail.html' template_name = 'profile_detail.html'
def get(self, request, pk): def get(self, request, pk):
profile = get_object_or_404(Profile, pk=pk) profile = get_object_or_404(Profile, pk=pk)
serializer = ProfileSerializer(profile) serializer = ProfileSerializer(profile)
return Response({'serializer': serializer, 'profile': profile}) return Response({'serializer': serializer, 'profile': profile})
def post(self, request, pk): def post(self, request, pk):
profile = get_object_or_404(Profile, pk=pk) profile = get_object_or_404(Profile, pk=pk)
serializer = ProfileSerializer(profile, data=request.data) serializer = ProfileSerializer(profile, data=request.data)
if not serializer.is_valid(): if not serializer.is_valid():
return Response({'serializer': serializer, 'profile': profile}) return Response({'serializer': serializer, 'profile': profile})
serializer.save() serializer.save()
return redirect('profile-list') return redirect('profile-list')
**profile_detail.html**: **profile_detail.html**:
{% load rest_framework %} {% load rest_framework %}
<html><body> <html><body>
<h1>Profile - {{ profile.name }}</h1> <h1>Profile - {{ profile.name }}</h1>
<form action="{% url 'profile-detail' pk=profile.pk %}" method="POST"> <form action="{% url 'profile-detail' pk=profile.pk %}" method="POST">
{% csrf_token %} {% csrf_token %}
{% render_form serializer %} {% render_form serializer %}
<input type="submit" value="Save"> <input type="submit" value="Save">
</form> </form>
</body></html> </body></html>
### Using template packs ### Using template packs
The `render_form` tag takes an optional `template_pack` argument, that specifies which template directory should be used for rendering the form and form fields. The `render_form` tag takes an optional `template_pack` argument, that specifies which template directory should be used for rendering the form and form fields.
REST framework includes three built-in template packs, all based on Bootstrap 3. The built-in styles are `horizontal`, `vertical`, and `inline`. The default style is `horizontal`. To use any of these template packs you'll want to also include the Bootstrap 3 CSS. REST framework includes three built-in template packs, all based on Bootstrap 3. The built-in styles are `horizontal`, `vertical`, and `inline`. The default style is `horizontal`. To use any of these template packs you'll want to also include the Bootstrap 3 CSS.
The following HTML will link to a CDN hosted version of the Bootstrap 3 CSS: The following HTML will link to a CDN hosted version of the Bootstrap 3 CSS:
<head> <head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
</head> </head>
Third party packages may include alternate template packs, by bundling a template directory containing the necessary form and field templates. Third party packages may include alternate template packs, by bundling a template directory containing the necessary form and field templates.
Let's take a look at how to render each of the three available template packs. For these examples we'll use a single serializer class to present a "Login" form. Let's take a look at how to render each of the three available template packs. For these examples we'll use a single serializer class to present a "Login" form.
class LoginSerializer(serializers.Serializer): class LoginSerializer(serializers.Serializer):
email = serializers.EmailField( email = serializers.EmailField(
max_length=100, max_length=100,
style={'placeholder': 'Email', 'autofocus': True} style={'placeholder': 'Email', 'autofocus': True}
) )
password = serializers.CharField( password = serializers.CharField(
max_length=100, max_length=100,
style={'input_type': 'password', 'placeholder': 'Password'} style={'input_type': 'password', 'placeholder': 'Password'}
) )
remember_me = serializers.BooleanField() remember_me = serializers.BooleanField()
--- ---
#### `rest_framework/vertical` #### `rest_framework/vertical`
Presents form labels above their corresponding control inputs, using the standard Bootstrap layout. Presents form labels above their corresponding control inputs, using the standard Bootstrap layout.
*This is the default template pack.* *This is the default template pack.*
{% load rest_framework %} {% load rest_framework %}
... ...
<form action="{% url 'login' %}" method="post" novalidate> <form action="{% url 'login' %}" method="post" novalidate>
{% csrf_token %} {% csrf_token %}
{% render_form serializer template_pack='rest_framework/vertical' %} {% render_form serializer template_pack='rest_framework/vertical' %}
<button type="submit" class="btn btn-default">Sign in</button> <button type="submit" class="btn btn-default">Sign in</button>
</form> </form>
![Vertical form example](../img/vertical.png) ![Vertical form example](../img/vertical.png)
--- ---
#### `rest_framework/horizontal` #### `rest_framework/horizontal`
Presents labels and controls alongside each other, using a 2/10 column split. Presents labels and controls alongside each other, using a 2/10 column split.
*This is the form style used in the browsable API and admin renderers.* *This is the form style used in the browsable API and admin renderers.*
{% load rest_framework %} {% load rest_framework %}
... ...
<form class="form-horizontal" action="{% url 'login' %}" method="post" novalidate> <form class="form-horizontal" action="{% url 'login' %}" method="post" novalidate>
{% csrf_token %} {% csrf_token %}
{% render_form serializer %} {% render_form serializer %}
<div class="form-group"> <div class="form-group">
<div class="col-sm-offset-2 col-sm-10"> <div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Sign in</button> <button type="submit" class="btn btn-default">Sign in</button>
</div> </div>
</div> </div>
</form> </form>
![Horizontal form example](../img/horizontal.png) ![Horizontal form example](../img/horizontal.png)
--- ---
#### `rest_framework/inline` #### `rest_framework/inline`
A compact form style that presents all the controls inline. A compact form style that presents all the controls inline.
{% load rest_framework %} {% load rest_framework %}
... ...
<form class="form-inline" action="{% url 'login' %}" method="post" novalidate> <form class="form-inline" action="{% url 'login' %}" method="post" novalidate>
{% csrf_token %} {% csrf_token %}
{% render_form serializer template_pack='rest_framework/inline' %} {% render_form serializer template_pack='rest_framework/inline' %}
<button type="submit" class="btn btn-default">Sign in</button> <button type="submit" class="btn btn-default">Sign in</button>
</form> </form>
![Inline form example](../img/inline.png) ![Inline form example](../img/inline.png)
## Field styles ## Field styles
Serializer fields can have their rendering style customized by using the `style` keyword argument. This argument is a dictionary of options that control the template and layout used. Serializer fields can have their rendering style customized by using the `style` keyword argument. This argument is a dictionary of options that control the template and layout used.
The most common way to customize the field style is to use the `base_template` style keyword argument to select which template in the template pack should be use. The most common way to customize the field style is to use the `base_template` style keyword argument to select which template in the template pack should be use.
For example, to render a `CharField` as an HTML textarea rather than the default HTML input, you would use something like this: For example, to render a `CharField` as an HTML textarea rather than the default HTML input, you would use something like this:
details = serializers.CharField( details = serializers.CharField(
max_length=1000, max_length=1000,
style={'base_template': 'textarea.html'} style={'base_template': 'textarea.html'}
) )
If you instead want a field to be rendered using a custom template that is *not part of an included template pack*, you can instead use the `template` style option, to fully specify a template name: If you instead want a field to be rendered using a custom template that is *not part of an included template pack*, you can instead use the `template` style option, to fully specify a template name:
details = serializers.CharField( details = serializers.CharField(
max_length=1000, max_length=1000,
style={'template': 'my-field-templates/custom-input.html'} style={'template': 'my-field-templates/custom-input.html'}
) )
Field templates can also use additional style properties, depending on their type. For example, the `textarea.html` template also accepts a `rows` property that can be used to affect the sizing of the control. Field templates can also use additional style properties, depending on their type. For example, the `textarea.html` template also accepts a `rows` property that can be used to affect the sizing of the control.
details = serializers.CharField( details = serializers.CharField(
max_length=1000, max_length=1000,
style={'base_template': 'textarea.html', 'rows': 10} style={'base_template': 'textarea.html', 'rows': 10}
) )
The complete list of `base_template` options and their associated style options is listed below. The complete list of `base_template` options and their associated style options is listed below.
base_template | Valid field types | Additional style options base_template | Valid field types | Additional style options
-----------------------|-------------------------------------------------------------|----------------------------------------------- -----------------------|-------------------------------------------------------------|-----------------------------------------------
input.html | Any string, numeric or date/time field | input_type, placeholder, hide_label, autofocus input.html | Any string, numeric or date/time field | input_type, placeholder, hide_label, autofocus
textarea.html | `CharField` | rows, placeholder, hide_label textarea.html | `CharField` | rows, placeholder, hide_label
select.html | `ChoiceField` or relational field types | hide_label select.html | `ChoiceField` or relational field types | hide_label
radio.html | `ChoiceField` or relational field types | inline, hide_label radio.html | `ChoiceField` or relational field types | inline, hide_label
select_multiple.html | `MultipleChoiceField` or relational fields with `many=True` | hide_label select_multiple.html | `MultipleChoiceField` or relational fields with `many=True` | hide_label
checkbox_multiple.html | `MultipleChoiceField` or relational fields with `many=True` | inline, hide_label checkbox_multiple.html | `MultipleChoiceField` or relational fields with `many=True` | inline, hide_label
checkbox.html | `BooleanField` | hide_label checkbox.html | `BooleanField` | hide_label
fieldset.html | Nested serializer | hide_label fieldset.html | Nested serializer | hide_label
list_fieldset.html | `ListField` or nested serializer with `many=True` | hide_label list_fieldset.html | `ListField` or nested serializer with `many=True` | hide_label

View File

@ -1,155 +1,155 @@
import pytest import pytest
from django.test import TestCase from django.test import TestCase
from rest_framework.compat import apply_markdown from rest_framework.compat import apply_markdown
from rest_framework.utils.formatting import dedent from rest_framework.utils.formatting import dedent
from rest_framework.views import APIView from rest_framework.views import APIView
# We check that docstrings get nicely un-indented. # We check that docstrings get nicely un-indented.
DESCRIPTION = """an example docstring DESCRIPTION = """an example docstring
==================== ====================
* list * list
* list * list
another header another header
-------------- --------------
code block code block
indented indented
# hash style header # # hash style header #
```json ```json
[{ [{
"alpha": 1, "alpha": 1,
"beta": "this is a string" "beta": "this is a string"
}] }]
```""" ```"""
# If markdown is installed we also test it's working # If markdown is installed we also test it's working
# (and that our wrapped forces '=' to h2 and '-' to h3) # (and that our wrapped forces '=' to h2 and '-' to h3)
MARKDOWN_DOCSTRING = """<h2 id="an-example-docstring">an example docstring</h2> MARKDOWN_DOCSTRING = """<h2 id="an-example-docstring">an example docstring</h2>
<ul> <ul>
<li>list</li> <li>list</li>
<li>list</li> <li>list</li>
</ul> </ul>
<h3 id="another-header">another header</h3> <h3 id="another-header">another header</h3>
<pre><code>code block <pre><code>code block
</code></pre> </code></pre>
<p>indented</p> <p>indented</p>
<h2 id="hash-style-header">hash style header</h2> <h2 id="hash-style-header">hash style header</h2>
<div class="highlight"><pre><span></span><span class="p">[{</span><br /><span class="w"> </span><span class="nt">&quot;alpha&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><br /><span class="w"> </span><span class="nt">&quot;beta&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;this is a string&quot;</span><br /><span class="p">}]</span><br /></pre></div> <div class="highlight"><pre><span></span><span class="p">[{</span><br /><span class="w"> </span><span class="nt">&quot;alpha&quot;</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><br /><span class="w"> </span><span class="nt">&quot;beta&quot;</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;this is a string&quot;</span><br /><span class="p">}]</span><br /></pre></div>
<p><br /></p>""" <p><br /></p>"""
class TestViewNamesAndDescriptions(TestCase): class TestViewNamesAndDescriptions(TestCase):
def test_view_name_uses_class_name(self): def test_view_name_uses_class_name(self):
""" """
Ensure view names are based on the class name. Ensure view names are based on the class name.
""" """
class MockView(APIView): class MockView(APIView):
pass pass
assert MockView().get_view_name() == 'Mock' assert MockView().get_view_name() == 'Mock'
def test_view_name_uses_name_attribute(self): def test_view_name_uses_name_attribute(self):
class MockView(APIView): class MockView(APIView):
name = 'Foo' name = 'Foo'
assert MockView().get_view_name() == 'Foo' assert MockView().get_view_name() == 'Foo'
def test_view_name_uses_suffix_attribute(self): def test_view_name_uses_suffix_attribute(self):
class MockView(APIView): class MockView(APIView):
suffix = 'List' suffix = 'List'
assert MockView().get_view_name() == 'Mock List' assert MockView().get_view_name() == 'Mock List'
def test_view_name_preferences_name_over_suffix(self): def test_view_name_preferences_name_over_suffix(self):
class MockView(APIView): class MockView(APIView):
name = 'Foo' name = 'Foo'
suffix = 'List' suffix = 'List'
assert MockView().get_view_name() == 'Foo' assert MockView().get_view_name() == 'Foo'
def test_view_description_uses_docstring(self): def test_view_description_uses_docstring(self):
"""Ensure view descriptions are based on the docstring.""" """Ensure view descriptions are based on the docstring."""
class MockView(APIView): class MockView(APIView):
"""an example docstring """an example docstring
==================== ====================
* list * list
* list * list
another header another header
-------------- --------------
code block code block
indented indented
# hash style header # # hash style header #
```json ```json
[{ [{
"alpha": 1, "alpha": 1,
"beta": "this is a string" "beta": "this is a string"
}] }]
```""" ```"""
assert MockView().get_view_description() == DESCRIPTION assert MockView().get_view_description() == DESCRIPTION
def test_view_description_uses_description_attribute(self): def test_view_description_uses_description_attribute(self):
class MockView(APIView): class MockView(APIView):
description = 'Foo' description = 'Foo'
assert MockView().get_view_description() == 'Foo' assert MockView().get_view_description() == 'Foo'
def test_view_description_allows_empty_description(self): def test_view_description_allows_empty_description(self):
class MockView(APIView): class MockView(APIView):
"""Description.""" """Description."""
description = '' description = ''
assert MockView().get_view_description() == '' assert MockView().get_view_description() == ''
def test_view_description_can_be_empty(self): def test_view_description_can_be_empty(self):
""" """
Ensure that if a view has no docstring, Ensure that if a view has no docstring,
then it's description is the empty string. then it's description is the empty string.
""" """
class MockView(APIView): class MockView(APIView):
pass pass
assert MockView().get_view_description() == '' assert MockView().get_view_description() == ''
def test_view_description_can_be_promise(self): def test_view_description_can_be_promise(self):
""" """
Ensure a view may have a docstring that is actually a lazily evaluated Ensure a view may have a docstring that is actually a lazily evaluated
class that can be converted to a string. class that can be converted to a string.
See: https://github.com/encode/django-rest-framework/issues/1708 See: https://github.com/encode/django-rest-framework/issues/1708
""" """
# use a mock object instead of gettext_lazy to ensure that we can't end # use a mock object instead of gettext_lazy to ensure that we can't end
# up with a test case string in our l10n catalog # up with a test case string in our l10n catalog
class MockLazyStr: class MockLazyStr:
def __init__(self, string): def __init__(self, string):
self.s = string self.s = string
def __str__(self): def __str__(self):
return self.s return self.s
class MockView(APIView): class MockView(APIView):
__doc__ = MockLazyStr("a gettext string") __doc__ = MockLazyStr("a gettext string")
assert MockView().get_view_description() == 'a gettext string' assert MockView().get_view_description() == 'a gettext string'
@pytest.mark.skipif(not apply_markdown, reason="Markdown is not installed") @pytest.mark.skipif(not apply_markdown, reason="Markdown is not installed")
def test_markdown(self): def test_markdown(self):
""" """
Ensure markdown to HTML works as expected. Ensure markdown to HTML works as expected.
""" """
assert apply_markdown(DESCRIPTION) == MARKDOWN_DOCSTRING assert apply_markdown(DESCRIPTION) == MARKDOWN_DOCSTRING
def test_dedent_tabs(): def test_dedent_tabs():
result = 'first string\n\nsecond string' result = 'first string\n\nsecond string'
assert dedent(" first string\n\n second string") == result assert dedent(" first string\n\n second string") == result
assert dedent("first string\n\n second string") == result assert dedent("first string\n\n second string") == result
assert dedent("\tfirst string\n\n\tsecond string") == result assert dedent("\tfirst string\n\n\tsecond string") == result
assert dedent("first string\n\n\tsecond string") == result assert dedent("first string\n\n\tsecond string") == result