Switch to mkdocs material theme for documentation (#9849)

* Switch to mkdocs material theme

* Add logo

* Style badges on homepage

* Basic dark theme

* Enable a few theme features

* Customise large logo for dark theme

* CSS tweaks

* Add background grid back

* Switch to mkdocs material theme

* Add logo

* Style badges on homepage

* Basic dark theme

* Add syntax highlighting to code snippets

* Convert homepage snippets to code fences

* Update homepage logos

* Move mkdocs-material to pyproject.toml docs group

* Keep existing syntax highlighting

* Remove old docs_theme folder

* Tweak syntax highlighting colors on dark theme

* Add readthedocs config file

* Fix end of file empty lines

* Upgrade pip during install

* Remove custom styling for .prettyprint

* Remove .prettyprint border entirely

* Make tabs sticky in navbar

* Merge page ToC with navigation

* Tweak colors for more accessible contrast

* Add Figma file for the logos

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Adjust styles for badges

* Enable content tabs for OS specific instructions

https://squidfunk.github.io/mkdocs-material/reference/content-tabs/

* Add icons for content tabs

* Standardize on .venv for virtualenv name in docs

* Add note about bash for Windows

---------

Co-authored-by: Asif Saif Uddin {"Auvi":"অভি"} <auvipy@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Bruno Alla 2026-03-01 19:59:00 +01:00 committed by GitHub
parent 40e77f0716
commit 190aae3c2d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 314 additions and 7712 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
docs/theme/src/drf-logos.fig filter=lfs diff=lfs merge=lfs -text

3
.gitignore vendored
View File

@ -1,7 +1,6 @@
*.pyc *.pyc
*.db *.db
*~ *~
.*
*.py.bak *.py.bak
@ -14,6 +13,8 @@
/env/ /env/
MANIFEST MANIFEST
coverage.* coverage.*
.coverage
.cache/
!.github !.github
!.gitignore !.gitignore

19
.readthedocs.yaml Normal file
View File

@ -0,0 +1,19 @@
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Set the OS, Python version, and other tools you might need
build:
os: ubuntu-24.04
tools:
python: "3.13"
jobs:
install:
- pip install --upgrade pip
- pip install -e . --group docs
# Build documentation with Mkdocs
mkdocs:
configuration: mkdocs.yml

BIN
docs/img/logo-dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
docs/img/logo-light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

View File

@ -17,7 +17,7 @@
} }
</style> </style>
<p class="badges" height=20px> <div class="badges">
<iframe src="https://ghbtns.com/github-btn.html?user=encode&amp;repo=django-rest-framework&amp;type=watch&amp;count=true" class="github-star-button" allowtransparency="true" frameborder="0" scrolling="0" width="110px" height="20px"></iframe> <iframe src="https://ghbtns.com/github-btn.html?user=encode&amp;repo=django-rest-framework&amp;type=watch&amp;count=true" class="github-star-button" allowtransparency="true" frameborder="0" scrolling="0" width="110px" height="20px"></iframe>
<a href="https://github.com/encode/django-rest-framework/actions/workflows/main.yml"> <a href="https://github.com/encode/django-rest-framework/actions/workflows/main.yml">
@ -27,11 +27,10 @@
<a href="https://pypi.org/project/djangorestframework/"> <a href="https://pypi.org/project/djangorestframework/">
<img src="https://img.shields.io/pypi/v/djangorestframework.svg" class="status-badge"> <img src="https://img.shields.io/pypi/v/djangorestframework.svg" class="status-badge">
</a> </a>
</p> </div>
--- ---
<p>
<h1 style="position: absolute; <h1 style="position: absolute;
width: 1px; width: 1px;
height: 1px; height: 1px;
@ -41,8 +40,8 @@
clip: rect(0,0,0,0); clip: rect(0,0,0,0);
border: 0;">Django REST Framework</h1> border: 0;">Django REST Framework</h1>
<img alt="Django REST Framework" title="Logo by Jake 'Sid' Smith" src="img/logo.png" width="600px" style="display: block; margin: 0 auto 0 auto"> ![Django REST Framework](img/logo-light.png#only-light)
</p> ![Django REST Framework](img/logo-dark.png#only-dark)
Django REST framework is a powerful and flexible toolkit for building Web APIs. Django REST framework is a powerful and flexible toolkit for building Web APIs.
@ -79,27 +78,35 @@ The following packages are optional:
Install using `pip`, including any optional packages you want... Install using `pip`, including any optional packages you want...
pip install djangorestframework ```bash
pip install markdown # Markdown support for the browsable API. pip install djangorestframework
pip install django-filter # Filtering support pip install markdown # Markdown support for the browsable API.
pip install django-filter # Filtering support
```
...or clone the project from github. ...or clone the project from github.
git clone https://github.com/encode/django-rest-framework ```bash
git clone https://github.com/encode/django-rest-framework
```
Add `'rest_framework'` to your `INSTALLED_APPS` setting. Add `'rest_framework'` to your `INSTALLED_APPS` setting.
INSTALLED_APPS = [ ```python
... INSTALLED_APPS = [
'rest_framework', # ...
] "rest_framework",
]
```
If you're intending to use the browsable API you'll probably also want to add REST framework's login and logout views. Add the following to your root `urls.py` file. If you're intending to use the browsable API you'll probably also want to add REST framework's login and logout views. Add the following to your root `urls.py` file.
urlpatterns = [ ```python
... urlpatterns = [
path('api-auth/', include('rest_framework.urls')) # ...
] path("api-auth/", include("rest_framework.urls"))
]
```
Note that the URL path can be whatever you want. Note that the URL path can be whatever you want.
@ -111,44 +118,51 @@ We'll create a read-write API for accessing information on the users of our proj
Any global settings for a REST framework API are kept in a single configuration dictionary named `REST_FRAMEWORK`. Start off by adding the following to your `settings.py` module: Any global settings for a REST framework API are kept in a single configuration dictionary named `REST_FRAMEWORK`. Start off by adding the following to your `settings.py` module:
REST_FRAMEWORK = { ```python
# Use Django's standard `django.contrib.auth` permissions, REST_FRAMEWORK = {
# or allow read-only access for unauthenticated users. # Use Django's standard `django.contrib.auth` permissions,
'DEFAULT_PERMISSION_CLASSES': [ # or allow read-only access for unauthenticated users.
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly' "DEFAULT_PERMISSION_CLASSES": [
] "rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly"
} ]
}
```
Don't forget to make sure you've also added `rest_framework` to your `INSTALLED_APPS`. Don't forget to make sure you've also added `rest_framework` to your `INSTALLED_APPS`.
We're ready to create our API now. We're ready to create our API now.
Here's our project's root `urls.py` module: Here's our project's root `urls.py` module:
from django.urls import path, include ```python
from django.contrib.auth.models import User from django.urls import path, include
from rest_framework import routers, serializers, viewsets from django.contrib.auth.models import User
from rest_framework import routers, serializers, viewsets
# Serializers define the API representation.
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ['url', 'username', 'email', 'is_staff']
# ViewSets define the view behavior. # Serializers define the API representation.
class UserViewSet(viewsets.ModelViewSet): class UserSerializer(serializers.HyperlinkedModelSerializer):
queryset = User.objects.all() class Meta:
serializer_class = UserSerializer model = User
fields = ["url", "username", "email", "is_staff"]
# Routers provide an easy way of automatically determining the URL conf.
router = routers.DefaultRouter()
router.register(r'users', UserViewSet)
# Wire up our API using automatic URL routing. # ViewSets define the view behavior.
# Additionally, we include login URLs for the browsable API. class UserViewSet(viewsets.ModelViewSet):
urlpatterns = [ queryset = User.objects.all()
path('', include(router.urls)), serializer_class = UserSerializer
path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
# Routers provide an easy way of automatically determining the URL conf.
router = routers.DefaultRouter()
router.register(r"users", UserViewSet)
# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
path("", include(router.urls)),
path("api-auth/", include("rest_framework.urls", namespace="rest_framework")),
]
```
You can now open the API in your browser at [http://127.0.0.1:8000/](http://127.0.0.1:8000/), and view your new 'users' API. If you use the login control in the top right corner you'll also be able to add, create and delete users from the system. You can now open the API in your browser at [http://127.0.0.1:8000/](http://127.0.0.1:8000/), and view your new 'users' API. If you use the login control in the top right corner you'll also be able to add, create and delete users from the system.

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
docs/theme/img/logo.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

13
docs/theme/main.html vendored Normal file
View File

@ -0,0 +1,13 @@
{% extends "base.html" %}
{% block scripts %}
{{ super() }}
<script>
document$.subscribe(function() {
document.querySelectorAll('pre code').forEach(code => {
code.parentElement.classList.add('prettyprint', 'well');
});
prettyPrint();
});
</script>
{% endblock %}

3
docs/theme/src/README.md vendored Normal file
View File

@ -0,0 +1,3 @@
# DRF logos
This folder contains the source file for the DRF logos as Figma file.

3
docs/theme/src/drf-logos.fig vendored Normal file
View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:762ff0dcedaa80a0ba95b9b8fc656d0c5fd2514a70d08335afe0eb06c9e14658
size 1303581

82
docs/theme/stylesheets/extra.css vendored Normal file
View File

@ -0,0 +1,82 @@
:root > * {
/* primary */
--md-primary-fg-color: #2c2c2c;
--md-primary-fg-color--light: #a8a8a8;
--md-primary-fg-color--dark: #181818;
/* accent */
--md-accent-fg-color: #c50d0d;
--md-accent-fg-color--light: #ff8f8f;
--md-accent-fg-color--dark: #A30000;
/* Style links */
--md-typeset-a-color: var(--md-typeset-color);
}
/* Dark theme customisation */
[data-md-color-scheme="slate"]
{
--md-accent-fg-color--dark: #F25757;
}
.md-header {
border-top: 5px solid #A30000;
}
body hr {
border-top: 1px dotted var(--md-accent-fg-color--dark);
}
.badges {
display: flex;
justify-content: end;
gap: 8px;
}
/* Cutesy quote styling */
[dir="ltr"] .md-typeset blockquote {
font-family: Georgia, serif;
font-size: 18px;
font-style: italic;
margin: 0.25em 0;
padding: 0.25em 40px;
line-height: 1.45;
position: relative;
color: var(--md-typeset-color);
border-left: none;
}
[dir="ltr"] .md-typeset blockquote:before {
display: block;
content: "\201C";
font-size: 80px;
position: absolute;
left: -10px;
top: -20px;
color: #7a7a7a;
}
[dir="ltr"] .md-typeset blockquote p:last-child {
color: #999999;
font-size: 14px;
display: block;
margin-top: 5px;
}
.md-typeset a {
color: var(--md-accent-fg-color--dark);
}
/* Replacement for `body { background-attachment: fixed; }`, which
has performance issues when scrolling on large displays. */
body::before {
content: ' ';
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-color: #f8f8f8;
background: url(../img/grid.png) repeat-x;
will-change: transform;
z-index: -1;
}

View File

@ -7,11 +7,16 @@
.typ, .atn, .dec, .var { color: teal; } .typ, .atn, .dec, .var { color: teal; }
.pln { color: #48484c; } .pln { color: #48484c; }
.prettyprint { [data-md-color-scheme="slate"]
padding: 8px; {
background-color: #f7f7f9; .com { color: #687272; }
border: 1px solid #e1e1e8; .lit { color: #2481c7; }
.str, .atv { color: #e37e8e;; }
.kwd, .prettyprint .tag { color: #6e8ee1; }
.typ, .atn, .dec, .var { color: #05abab; }
.pln { color: #d3d3dc; }
} }
.prettyprint.linenums { .prettyprint.linenums {
-webkit-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; -webkit-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0;
-moz-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; -moz-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0;

View File

@ -11,12 +11,23 @@ The tutorial is fairly in-depth, so you should probably get a cookie and a cup o
## Setting up a new environment ## Setting up a new environment
Before we do anything else we'll create a new virtual environment, using [venv]. This will make sure our package configuration is kept nicely isolated from any other projects we're working on. Before we do anything else we'll create a new virtual environment called `.venv`, using [venv]. This will make sure our package configuration is kept nicely isolated from any other projects we're working on.
```bash === ":fontawesome-brands-linux: Linux, :fontawesome-brands-apple: macOS"
python3 -m venv env
source env/bin/activate # On Windows use `env\Scripts\activate` ```bash
``` python3 -m venv .venv
source .venv/bin/activate
```
=== ":fontawesome-brands-windows: Windows"
If you use Bash for Windows
```bash
python3 -m venv .venv
source .venv\Scripts\activate
```
Now that we're inside a virtual environment, we can install our package requirements. Now that we're inside a virtual environment, we can install our package requirements.

View File

@ -6,24 +6,49 @@ We're going to create a simple API to allow admin users to view and edit the use
Create a new Django project named `tutorial`, then start a new app called `quickstart`. Create a new Django project named `tutorial`, then start a new app called `quickstart`.
```bash === ":fontawesome-brands-linux: Linux, :fontawesome-brands-apple: macOS"
# Create the project directory
mkdir tutorial
cd tutorial
# Create a virtual environment to isolate our package dependencies locally ```bash
python3 -m venv env # Create the project directory
source env/bin/activate # On Windows use `env\Scripts\activate` mkdir tutorial
cd tutorial
# Create a virtual environment to isolate our package dependencies locally
python3 -m venv .venv
source .venv/bin/activate
# Install Django and Django REST framework into the virtual environment
pip install djangorestframework
# Set up a new project with a single application
django-admin startproject tutorial . # Note the trailing '.' character
cd tutorial
django-admin startapp quickstart
cd ..
```
# Install Django and Django REST framework into the virtual environment === ":fontawesome-brands-windows: Windows"
pip install djangorestframework
# Set up a new project with a single application If you use Bash for Windows
django-admin startproject tutorial . # Note the trailing '.' character
cd tutorial ```bash
django-admin startapp quickstart # Create the project directory
cd .. mkdir tutorial
``` cd tutorial
# Create a virtual environment to isolate our package dependencies locally
python3 -m venv .venv
source .venv\Scripts\activate
# Install Django and Django REST framework into the virtual environment
pip install djangorestframework
# Set up a new project with a single application
django-admin startproject tutorial . # Note the trailing '.' character
cd tutorial
django-admin startapp quickstart
cd ..
```
The project layout should look like: The project layout should look like:

View File

@ -1,9 +0,0 @@
{% extends "main.html" %}
{% block content %}
<h1 id="404-page-not-found" style="text-align: center">404</h1>
<p style="text-align: center"><strong>Page not found</strong></p>
<p style="text-align: center">Try the <a href="{{ base_url }}">homepage</a>, or <a href="#mkdocs_search_modal" data-toggle="modal">search the documentation</a>.</p>
{% endblock %}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,30 +0,0 @@
.copy-block-button {
position: absolute;
top: 6px;
right: 6px;
z-index: 10;
white-space: nowrap; /* Prevent text wrap */
}
/* Ensure the PRE container provides positioning context */
pre {
position: relative;
padding-top: 35px; /* Room for the button */
overflow-x: auto; /* Allow horizontal scrolling */
}
/* Code block scrollable */
pre code {
display: block;
overflow-x: auto;
}
/*
The MkDocs/DRF theme injects a <span> inside buttons and applies
a text color that overrides btn-inverse defaults.
This override is intentionally scoped and limited to color only.
*/
.copy-block-button,
.copy-block-button span {
color: #ffffff !important;
}

View File

@ -1,473 +0,0 @@
/* Set the body padding-top when above 980px to push the content down from
below the navbar, which is fixed at >980px screen widths. */
pre {
font-size: 12px;
}
.dropdown .dropdown-menu {
display: none;
overflow-y: auto;
}
.dropdown.open .dropdown-menu {
display: block;
}
@media (max-width: 480px) {
.repo-link {
display: none;
}
}
/* Header link to GitHub */
.repo-link {
float: right;
margin-right: 10px;
margin-top: 9px;
}
body.index-page #main-content p.badges {
padding-bottom: 1px;
}
/* GitHub 'Star' badge */
body.index-page #main-content iframe.github-star-button {
float: right;
margin-top: -12px;
margin-right: -15px;
}
/* CI and PyPI badge */
body.index-page #main-content img.status-badge {
float: right;
margin-right: 8px;
margin-top: -11px;
margin-bottom: 0px;
}
/* Github source file badges */
a.github {
float: right;
margin-top: -12px;
margin-right: 12px;
}
a.github:hover {
text-decoration: none;
}
/* */
body hr {
border-top: 1px dotted #A30000;
}
/* Force TOC text to not overrun */
#table-of-contents {
overflow: hidden;
margin: 0 0 20px 0;
}
/* Code blocks should scroll horizontally */
pre {
overflow: auto;
word-wrap: normal;
white-space: pre;
}
code, pre {
font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif;
font-size: 13px;
}
/* Preserve the spacing of the navbar across different screen sizes. */
.navbar-inner {
/*padding: 5px 0;*/
}
@media (max-width: 979px) {
.navbar .brand {
margin-left: 0;
padding-left: 0;
}
.navbar-inner .container-fluid {
padding-left: 15px;
}
}
.nav-list li.main {
font-weight: bold;
}
.nav-list a {
overflow: hidden;
}
.nav-list > li > a {
padding: 2px 15px 3px;
}
/* Set the table of contents to static so it flows back into the content when
viewed on tablets and smaller. */
@media (max-width: 767px) {
#table-of-contents {
position: static;
}
}
/* When the page is in two-column layout, give the main content some room
to breath on the left. */
@media (min-width: 768px) {
#main-content {
padding-left: 1em;
}
}
/* Cutesy quote styling */
blockquote {
font-family: Georgia, serif;
font-size: 18px;
font-style: italic;
margin: 0.25em 0;
padding: 0.25em 40px;
line-height: 1.45;
position: relative;
color: #383838;
border-left: none;
}
blockquote:before {
display: block;
content: "\201C";
font-size: 80px;
position: absolute;
left: -10px;
top: -20px;
color: #7a7a7a;
}
blockquote p:last-child {
color: #999999;
font-size: 14px;
display: block;
margin-top: 5px;
}
/*=== dabapps bootstrap styles ====*/
html{
width:100%;
background: none;
}
body, .navbar .navbar-inner .container-fluid{
max-width: 1150px;
margin: 0 auto;
}
/* Replacement for `body { background-attachment: fixed; }`, which
has performance issues when scrolling on large displays. */
body::before {
content: ' ';
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-color: #f8f8f8;
background: url(../img/grid.png) repeat-x;
will-change: transform;
z-index: -1;
}
#main-content h1:first-of-type {
margin-top: 0
}
#main-content h1, #main-content h2 {
font-weight: 300;
margin-top: 20px
}
#main-content h3, #main-content h4, #main-content h5 {
font-weight: 300;
margin-top: 15px
}
#main-content img {
display: block;
margin: 40px auto;
}
/* custom navigation styles */
.navbar .navbar-inner{
background: #2C2C2C;
color: white;
border: none;
border-top: 5px solid #A30000;
}
.navbar .navbar-inner .nav li, .navbar .navbar-inner .nav li a, .navbar .navbar-inner .brand{
color: white;
}
.nav-list > .active > a, .navbar .navbar-inner .nav li a:hover {
background:#212121;
color:white;
}
.navbar .navbar-inner .dropdown-menu li a, .navbar .navbar-inner .dropdown-menu li{
color: #A30000;
}
.dropdown-menu .active > a,
.dropdown-menu .active > a:hover {
background-image: none;
}
.navbar-inverse .nav .dropdown .active > a,
.navbar-inverse .nav .dropdown .active > a:hover,
.navbar-inverse .nav .dropdown .active > a:focus {
background-color: #eeeeee;
}
.navbar .navbar-inner .dropdown-menu li a:hover{
background: #eeeeee;
color: #c20000;
}
/* custom general page styles */
.hero-unit h2, .hero-unit h1{
color: #A30000;
}
body a{
color: #A30000;
}
body a:hover{
color: #c20000;
}
/* subnavigation styles */
@media (min-width: 767px) {
.sidebar-nav-fixed {
position:fixed;
width:19%;
max-width: 240px;
}
.navbar {
position: fixed;
}
.navbar .navbar-inner .container-fluid{
max-width: 1110px;
}
}
h1 code, h2 code, h3 code, h4 code, h5 code {
color: #333;
}
/* sticky footer and footer */
html, body {
height: 100%;
}
.wrapper {
min-height: 100%;
height: auto !important;
height: 100%;
margin: 0 auto -60px;
}
.body-content{
padding-top: 70px;
padding-bottom: 70px;
}
@media (max-width: 979px) {
.navbar-fixed-top .navbar-inner {
padding: 0px;
}
}
@media (max-width: 767px) {
.body-content{
padding-top: 0px;
}
}
@media (min-width: 768px) {
footer.span12 {
width: 95%;
}
}
footer, .push {
height: 60px; /* .push must be the same height as .footer */
}
footer p {
text-align: center;
color: gray;
border-top: 1px solid #DDD;
padding-top: 10px;
}
footer a {
color: gray;
font-weight: bold;
}
footer a:hover {
color: gray;
}
.btn-inverse {
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#606060), to(#404040)) !important;
background-image: -webkit-linear-gradient(top, #606060, #404040) !important;
}
.modal-open .modal,.btn:focus{outline:none;}
@media (max-width: 650px) {
.repo-link.btn-inverse {display: none;}
}
td, th {
padding: 0.25em;
background-color: #f7f7f9;
border-color: #e1e1e8;
}
table {
border-color: white;
margin-bottom: 0.6em;
}
.side-nav {
overflow-y: scroll;
}
ul.sponsor.diamond li a {
float: left;
width: 600px;
height: 20px;
text-align: center;
margin: 10px 70px;
padding: 300px 0 0 0;
background-position: 0 50%;
background-size: 600px auto;
background-repeat: no-repeat;
font-size: 200%;
}
@media (max-width: 1000px) {
ul.sponsor.diamond li a {
float: left;
width: 300px;
height: 20px;
text-align: center;
margin: 10px 40px;
padding: 300px 0 0 0;
background-position: 0 50%;
background-size: 280px auto;
background-repeat: no-repeat;
font-size: 150%;
}
}
ul.sponsor.platinum li a {
float: left;
width: 300px;
height: 20px;
text-align: center;
margin: 10px 40px;
padding: 300px 0 0 0;
background-position: 0 50%;
background-size: 280px auto;
background-repeat: no-repeat;
font-size: 150%;
}
ul.sponsor.gold 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%;
}
ul.sponsor.silver 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%;
}
ul.sponsor {
list-style: none;
display: block;
}
#mkdocs_search_modal article p{
word-wrap: break-word;
}
.toclink {
color: #333;
}
.book-cover img {
margin: 0 !important;
display: inline-block !important;
}
/* admonition */
.admonition {
border: .075rem solid #448aff;
border-radius: .2rem;
margin: 1.5625em 0;
padding: 0 .6rem;
}
.admonition-title {
background: #448aff1a;
font-weight: 700;
margin: 0 -.6rem 1em;
padding: 0.4rem 0.6rem;
}
.admonition.tip {
border: .075rem solid #1e8d21;
}
.admonition.tip .admonition-title {
background: #1e8d211a;
}
.admonition.warning {
border: .075rem solid #ff9844;
}
.admonition.warning .admonition-title {
background: #ff98441a;
}
.admonition.danger {
border: .075rem solid #f63a3a;
}
.admonition.danger .admonition-title {
background: #f63a3a1a;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

View File

@ -1,24 +0,0 @@
document.addEventListener("DOMContentLoaded", function () {
document.querySelectorAll("pre > code").forEach(function (codeBlock) {
const button = document.createElement("button");
button.className = "copy-block-button btn btn-inverse btn-mini";
button.type = "button";
button.textContent = "Copy";
button.addEventListener("click", function () {
navigator.clipboard.writeText(codeBlock.textContent)
.then(() => {
button.textContent = "Copied!";
setTimeout(() => button.textContent = "Copy", 1200);
})
.catch(() => {
button.textContent = "Failed";
setTimeout(() => button.textContent = "Copy", 1200);
});
});
const pre = codeBlock.parentNode;
pre.style.position = "relative";
pre.appendChild(button);
});
});

File diff suppressed because one or more lines are too long

View File

@ -1,29 +0,0 @@
var getSearchTerm = function() {
var sPageURL = window.location.search.substring(1);
var sURLVariables = sPageURL.split('&');
for (var i = 0; i < sURLVariables.length; i++) {
var sParameterName = sURLVariables[i].split('=');
if (sParameterName[0] === 'q') {
return sParameterName[1];
}
}
};
$(function() {
var searchTerm = getSearchTerm(),
$searchModal = $('#mkdocs_search_modal'),
$searchQuery = $searchModal.find('#mkdocs-search-query'),
$searchResults = $searchModal.find('#mkdocs-search-results');
$('pre code').parent().addClass('prettyprint well');
if (searchTerm) {
$searchQuery.val(searchTerm);
$searchResults.text('Searching...');
$searchModal.modal();
}
$searchModal.on('shown', function() {
$searchQuery.focus();
});
});

View File

@ -1,174 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<title>{% if page.title %}{{ page.title }} - {% endif %}{{ config.site_name }}</title>
<link href="{{ 'img/favicon.ico'|url }}" rel="icon" type="image/x-icon">
<link rel="canonical" href="{{ page.canonical_url|url }}" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Django, API, REST{% if page %}, {{ page.title }}{% endif %}">
<meta name="author" content="Tom Christie">
<!-- Le styles -->
<link href="{{ 'css/prettify.css'|url }}" rel="stylesheet">
<link href="{{ 'css/bootstrap.css'|url }}" rel="stylesheet">
<link href="{{ 'css/bootstrap-responsive.css'|url }}" rel="stylesheet">
<link href="{{ 'css/default.css'|url }}" rel="stylesheet">
{% for path in config.extra_css %}
<link href="{{ path|url }}" rel="stylesheet">
{% endfor %}
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-18852272-2']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
})();
</script>
<style>
#sidebarInclude img {
margin-bottom: 10px;
}
#sidebarInclude a.promo {
color: black;
}
@media (max-width: 767px) {
div.promo {
display: none;
}
}
</style>
</head>
<body onload="prettyPrint()" class="{% if page and page.is_homepage %}index{% endif %}-page">
<div class="wrapper">
{% include "nav.html" %}
<div class="body-content">
<div class="container-fluid">
<!-- Search Modal -->
<div id="mkdocs_search_modal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3 id="myModalLabel">Documentation search</h3>
</div>
<div class="modal-body">
<form role="form" autocomplete="off">
<div class="form-group">
<input type="text" name="q" class="form-control" placeholder="Search..." id="mkdocs-search-query">
</div>
</form>
<div id="mkdocs-search-results"></div>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
</div>
</div>
<div class="row-fluid">
<div class="span3">
<div id="table-of-contents">
<ul class="nav nav-list side-nav well sidebar-nav-fixed">
{% if page and page.is_homepage %}
<li class="main">
<a href="#">Django REST framework</a>
</li>
{% endif %}
{% for toc_item in page.toc %}
<li class="{% if page and not page.is_homepage %}main{% endif %}">
<a href="{{ toc_item.url }}">{{ toc_item.title }}</a>
</li>
{% for toc_item in toc_item.children %}
<li>
<a href="{{ toc_item.url }}">{{ toc_item.title }}</a>
</li>
{% endfor %}
{% endfor %}
<div class="promo">
{% if page.toc %}<hr/>{% endif %}
<div id="sidebarInclude">
</div>
</ul>
</div>
</div>
<div id="main-content" class="span9">
{% block content %}
{% if page.meta.source %}
{% for filename in page.meta.source %}
<a class="github" href="https://github.com/encode/django-rest-framework/tree/main/rest_framework/{{ filename }}">
<span class="label label-info">{{ filename }}</span>
</a>
{% endfor %}
{% endif %}
{{ page.content }}
{% endblock %}
</div> <!--/span-->
</div> <!--/row-->
</div> <!--/.fluid-container-->
</div> <!--/.body content-->
<div id="push"></div>
</div> <!--/.wrapper -->
<footer class="span12">
<p>Documentation built with <a href="http://www.mkdocs.org/">MkDocs</a>.
</p>
</footer>
<!-- Le javascript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script async src="https://fund.django-rest-framework.org/sidebar_include.js"></script>
<script src="{{ 'js/jquery-1.8.1-min.js'|url }}"></script>
<script src="{{ 'js/prettify-1.0.js'|url }}"></script>
<script src="{{ 'js/bootstrap-2.1.1-min.js'|url }}"></script>
<script src="{{ 'js/theme.js'|url }}"></script>
<script>var base_url = '{{ base_url }}';</script>
{% for path in config.extra_javascript %}
<script src="{{ path|url }}" defer></script>
{% endfor %}
<script>
var shiftWindow = function() {
scrollBy(0, -50)
};
if (location.hash) shiftWindow();
window.addEventListener("hashchange", shiftWindow);
$('.dropdown-menu').on('click touchstart', function(event) {
event.stopPropagation();
});
// Dynamically force sidenav/dropdown to no higher than browser window
$('.side-nav, .dropdown-menu').css('max-height', window.innerHeight - 130);
$(function() {
$(window).resize(function() {
$('.side-nav, .dropdown-menu').css('max-height', window.innerHeight - 130);
});
});
</script>
</body>
</html>

View File

@ -1,46 +0,0 @@
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner">
<div class="container-fluid">
<a class="repo-link btn btn-primary btn-small" href="https://github.com/encode/django-rest-framework">GitHub</a>
<a class="repo-link btn btn-inverse btn-small {% if not page.next_page %}disabled{% endif %}" rel="next" {% if page.next_page %}href="{{ page.next_page.url|url }}"{% endif %}>
Next <i class="icon-arrow-right icon-white"></i>
</a>
<a class="repo-link btn btn-inverse btn-small {% if not page.previous_page %}disabled{% endif %}" rel="prev" {% if page.previous_page %}href="{{ page.previous_page.url|url }}"{% endif %}>
<i class="icon-arrow-left icon-white"></i> Previous
</a>
<a id="search_modal_show" class="repo-link btn btn-inverse btn-small" href="#mkdocs_search_modal" data-toggle="modal" data-target="#mkdocs_search_modal"><i class="icon-search icon-white"></i> Search</a>
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a class="brand" href="https://www.django-rest-framework.org/">Django REST framework</a>
<div class="nav-collapse collapse">
{% if nav|length>1 %}
<!-- Main navigation -->
<ul class="nav navbar-nav">
{% for nav_item in nav %} {% if nav_item.children %}
<li class="dropdown{% if nav_item.active %} active{% endif %}">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ nav_item.title }} <b class="caret"></b></a>
<ul class="dropdown-menu">
{% for nav_item in nav_item.children %}
<li {% if nav_item.active %}class="active" {% endif %}>
<a href="{{ nav_item.url|url }}">{{ nav_item.title }}</a>
</li>
{% endfor %}
</ul>
</li>
{% else %}
<li {% if nav_item.active %}class="active" {% endif %}>
<a href="{{ nav_item.url|url }}">{{ nav_item.title }}</a>
</li>
{% endif %} {% endfor %}
</ul>
{% endif %}
</div>
<!--/.nav-collapse -->
</div>
</div>
</div>

View File

@ -5,17 +5,73 @@ site_description: Django REST framework - Web APIs for Django
repo_url: https://github.com/encode/django-rest-framework repo_url: https://github.com/encode/django-rest-framework
theme: theme:
name: mkdocs name: material
custom_dir: docs_theme custom_dir: docs/theme
favicon: theme/img/favicon.ico
logo: theme/img/logo.png
palette:
- media: "(prefers-color-scheme)"
primary: custom
accent: custom
toggle:
icon: material/brightness-auto
name: "Switch to light mode"
- media: "(prefers-color-scheme: light)"
scheme: default
primary: custom
accent: custom
toggle:
icon: material/brightness-7
name: "Switch to dark mode"
- media: "(prefers-color-scheme: dark)"
scheme: slate
primary: custom
accent: custom
toggle:
icon: material/brightness-4
name: "Switch to system preference"
features:
- content.tabs.link
- content.code.annotate
- content.code.copy
- navigation.tabs
- navigation.tabs.sticky
- navigation.instant
- navigation.instant.prefetch
- navigation.instant.progress
- navigation.path
- navigation.sections
- navigation.top
- navigation.tracking
- search.suggest
- toc.follow
- toc.integrate
extra_css:
- theme/stylesheets/extra.css
- theme/stylesheets/prettify.css
extra_javascript:
- theme/js/prettify-1.0.js
markdown_extensions: markdown_extensions:
- admonition - admonition
- toc: - attr_list
anchorlink: True - toc:
permalink: true
- pymdownx.highlight:
pygments_lang_class: true
- pymdownx.inlinehilite
- pymdownx.snippets
- pymdownx.superfences
- pymdownx.tabbed:
alternate_style: true
- pymdownx.emoji:
emoji_index: !!python/name:material.extensions.emoji.twemoji
emoji_generator: !!python/name:material.extensions.emoji.to_svg
nav: nav:
- Home: 'index.md' - Home: 'index.md'
- Tutorial: - Tutorial:
- 'Quickstart': 'tutorial/quickstart.md' - 'Quickstart': 'tutorial/quickstart.md'
- '1 - Serialization': 'tutorial/1-serialization.md' - '1 - Serialization': 'tutorial/1-serialization.md'
- '2 - Requests and responses': 'tutorial/2-requests-and-responses.md' - '2 - Requests and responses': 'tutorial/2-requests-and-responses.md'
@ -23,7 +79,7 @@ nav:
- '4 - Authentication and permissions': 'tutorial/4-authentication-and-permissions.md' - '4 - Authentication and permissions': 'tutorial/4-authentication-and-permissions.md'
- '5 - Relationships and hyperlinked APIs': 'tutorial/5-relationships-and-hyperlinked-apis.md' - '5 - Relationships and hyperlinked APIs': 'tutorial/5-relationships-and-hyperlinked-apis.md'
- '6 - Viewsets and routers': 'tutorial/6-viewsets-and-routers.md' - '6 - Viewsets and routers': 'tutorial/6-viewsets-and-routers.md'
- API Guide: - API Guide:
- 'Requests': 'api-guide/requests.md' - 'Requests': 'api-guide/requests.md'
- 'Responses': 'api-guide/responses.md' - 'Responses': 'api-guide/responses.md'
- 'Views': 'api-guide/views.md' - 'Views': 'api-guide/views.md'
@ -52,7 +108,7 @@ nav:
- 'Status codes': 'api-guide/status-codes.md' - 'Status codes': 'api-guide/status-codes.md'
- 'Testing': 'api-guide/testing.md' - 'Testing': 'api-guide/testing.md'
- 'Settings': 'api-guide/settings.md' - 'Settings': 'api-guide/settings.md'
- Topics: - Topics:
- 'Documenting your API': 'topics/documenting-your-api.md' - 'Documenting your API': 'topics/documenting-your-api.md'
- 'Internationalization': 'topics/internationalization.md' - 'Internationalization': 'topics/internationalization.md'
- 'AJAX, CSRF & CORS': 'topics/ajax-csrf-cors.md' - 'AJAX, CSRF & CORS': 'topics/ajax-csrf-cors.md'
@ -60,7 +116,7 @@ nav:
- 'Browser Enhancements': 'topics/browser-enhancements.md' - 'Browser Enhancements': 'topics/browser-enhancements.md'
- 'The Browsable API': 'topics/browsable-api.md' - 'The Browsable API': 'topics/browsable-api.md'
- 'REST, Hypermedia & HATEOAS': 'topics/rest-hypermedia-hateoas.md' - 'REST, Hypermedia & HATEOAS': 'topics/rest-hypermedia-hateoas.md'
- Community: - Community:
- 'Tutorials and Resources': 'community/tutorials-and-resources.md' - 'Tutorials and Resources': 'community/tutorials-and-resources.md'
- 'Third Party Packages': 'community/third-party-packages.md' - 'Third Party Packages': 'community/third-party-packages.md'
- 'Contributing to REST framework': 'community/contributing.md' - 'Contributing to REST framework': 'community/contributing.md'
@ -86,9 +142,3 @@ nav:
- 'Kickstarter Announcement': 'community/kickstarter-announcement.md' - 'Kickstarter Announcement': 'community/kickstarter-announcement.md'
- 'Mozilla Grant': 'community/mozilla-grant.md' - 'Mozilla Grant': 'community/mozilla-grant.md'
- 'Jobs': 'community/jobs.md' - 'Jobs': 'community/jobs.md'
extra_css:
- css/copy-button.css
extra_javascript:
- js/copy-button.js

View File

@ -57,6 +57,7 @@ test = [
docs = [ docs = [
# MkDocs to build our documentation. # MkDocs to build our documentation.
"mkdocs==1.6.1", "mkdocs==1.6.1",
"mkdocs-material[imaging]==9.7.0",
# pylinkvalidator to check for broken links in documentation. # pylinkvalidator to check for broken links in documentation.
"pylinkvalidator==0.3", "pylinkvalidator==0.3",
] ]