├── tests ├── __init__.py ├── browsable_api │ ├── __init__.py │ ├── no_auth_urls.py │ ├── auth_urls.py │ ├── views.py │ └── test_browsable_api.py ├── urls.py ├── test_settings.py ├── description.py ├── test_write_only_fields.py ├── test_serializer_nested.py ├── test_status.py ├── test_middleware.py ├── test_viewsets.py ├── test_reverse.py ├── test_negotiation.py ├── conftest.py ├── models.py ├── utils.py ├── test_multitable_inheritance.py ├── test_templatetags.py ├── test_bound_fields.py ├── test_urlpatterns.py ├── test_relations_generic.py ├── test_parsers.py ├── test_description.py └── test_serializer_bulk_update.py ├── rest_framework ├── utils │ ├── __init__.py │ ├── urls.py │ ├── humanize_datetime.py │ ├── formatting.py │ ├── breadcrumbs.py │ ├── html.py │ ├── encoders.py │ ├── mediatypes.py │ ├── representation.py │ └── serializer_helpers.py ├── authtoken │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ └── 0001_initial.py │ ├── south_migrations │ │ ├── __init__.py │ │ └── 0001_initial.py │ ├── admin.py │ ├── views.py │ ├── serializers.py │ └── models.py ├── templatetags │ └── __init__.py ├── models.py ├── templates │ └── rest_framework │ │ ├── inline │ │ ├── list_fieldset.html │ │ ├── fieldset.html │ │ ├── checkbox.html │ │ ├── textarea.html │ │ ├── form.html │ │ ├── input.html │ │ ├── checkbox_multiple.html │ │ ├── select.html │ │ ├── select_multiple.html │ │ └── radio.html │ │ ├── api.html │ │ ├── login.html │ │ ├── vertical │ │ ├── list_fieldset.html │ │ ├── fieldset.html │ │ ├── form.html │ │ ├── checkbox.html │ │ ├── textarea.html │ │ ├── input.html │ │ ├── select.html │ │ ├── select_multiple.html │ │ ├── checkbox_multiple.html │ │ └── radio.html │ │ ├── api_form.html │ │ ├── raw_data_form.html │ │ ├── pagination │ │ ├── previous_and_next.html │ │ └── numbers.html │ │ ├── horizontal │ │ ├── fieldset.html │ │ ├── form.html │ │ ├── list_fieldset.html │ │ ├── checkbox.html │ │ ├── textarea.html │ │ ├── input.html │ │ ├── select.html │ │ ├── select_multiple.html │ │ ├── checkbox_multiple.html │ │ └── radio.html │ │ └── login_base.html ├── locale │ ├── ar │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── cs │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── da │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── de │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── en │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── es │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── et │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── fr │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── hu │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── id │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── it │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── mk │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── nl │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── pl │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── ru │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── sk │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── sv │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── tr │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── uk │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── vi │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── en_US │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── ko_KR │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── pt_BR │ │ └── LC_MESSAGES │ │ │ └── django.mo │ ├── pt_PT │ │ └── LC_MESSAGES │ │ │ └── django.mo │ └── zh_CN │ │ └── LC_MESSAGES │ │ └── django.mo ├── static │ └── rest_framework │ │ ├── img │ │ ├── grid.png │ │ ├── glyphicons-halflings.png │ │ └── glyphicons-halflings-white.png │ │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.ttf │ │ └── glyphicons-halflings-regular.woff │ │ ├── css │ │ ├── prettify.css │ │ ├── default.css │ │ └── bootstrap-tweaks.css │ │ └── js │ │ └── default.js ├── __init__.py ├── urls.py ├── reverse.py ├── status.py ├── urlpatterns.py ├── mixins.py ├── response.py └── negotiation.py ├── setup.cfg ├── docs ├── CNAME ├── img │ ├── logo.png │ ├── apiary.png │ ├── slate.png │ ├── cerulean.png │ ├── quickstart.png │ ├── sponsors │ │ ├── 2-sga.png │ │ ├── 3-aba.png │ │ ├── 3-isl.png │ │ ├── 1-cyan.png │ │ ├── 1-divio.png │ │ ├── 1-lulu.png │ │ ├── 2-byte.png │ │ ├── 2-crate.png │ │ ├── 2-hipo.png │ │ ├── 2-vinta.png │ │ ├── 3-blimp.png │ │ ├── 3-garfo.png │ │ ├── 3-holvi.png │ │ ├── 3-tivix.png │ │ ├── 1-potato.png │ │ ├── 1-runscope.png │ │ ├── 2-compile.png │ │ ├── 2-cryptico.png │ │ ├── 2-django.png │ │ ├── 2-heroku.png │ │ ├── 2-hipflask.png │ │ ├── 2-laterpay.png │ │ ├── 2-nexthub.png │ │ ├── 2-opbeat.png │ │ ├── 2-rapasso.png │ │ ├── 2-sirono.png │ │ ├── 2-wusawork.png │ │ ├── 3-aditium.png │ │ ├── 3-beefarm.png │ │ ├── 3-cantemo.gif │ │ ├── 3-gizmag.png │ │ ├── 3-nephila.png │ │ ├── 3-openeye.png │ │ ├── 3-phurba.png │ │ ├── 3-pkgfarm.png │ │ ├── 3-safari.png │ │ ├── 3-shippo.png │ │ ├── 3-teonite.png │ │ ├── 3-vzzual.png │ │ ├── 3-wildfish.png │ │ ├── 0-eventbrite.png │ │ ├── 1-kuwaitnet.png │ │ ├── 1-purplebit.png │ │ ├── 1-wiredrive.png │ │ ├── 2-prorenata.png │ │ ├── 2-pulsecode.png │ │ ├── 3-alwaysdata.png │ │ ├── 3-brightloop.png │ │ ├── 3-fluxility.png │ │ ├── 3-ipushpull.png │ │ ├── 3-makespace.png │ │ ├── 3-pathwright.png │ │ ├── 3-providenz.png │ │ ├── 3-trackmaven.png │ │ ├── 3-transcode.png │ │ ├── 1-simple-energy.png │ │ ├── 2-koordinates.png │ │ ├── 2-singing-horse.png │ │ ├── 3-ax_semantics.png │ │ ├── 3-infinite_code.png │ │ ├── 3-life_the_game.png │ │ ├── 2-lightning_kite.png │ │ ├── 2-mirus_research.png │ │ ├── 2-schuberg_philis.png │ │ ├── 3-thermondo-gmbh.png │ │ ├── 1-vokal_interactive.png │ │ ├── 2-rheinwerk_verlag.png │ │ ├── 2-security_compass.png │ │ ├── 3-crosswordtracker.png │ │ ├── 3-triggered_messaging.png │ │ └── 3-imt_computer_services.png │ ├── travis-status.png │ ├── pages-pagination.png │ ├── self-describing.png │ ├── cursor-pagination.png │ ├── django-rest-swagger.png │ ├── rest-framework-docs.png │ ├── labels-and-milestones.png │ └── link-header-pagination.png ├── topics │ ├── writable-nested-serializers.md │ ├── ajax-csrf-cors.md │ ├── browser-enhancements.md │ └── rest-hypermedia-hateoas.md └── api-guide │ ├── reverse.md │ ├── format-suffixes.md │ └── metadata.md ├── requirements ├── requirements-documentation.txt ├── requirements-testing.txt ├── requirements-codestyle.txt ├── requirements-optionals.txt └── requirements-packaging.txt ├── docs_theme ├── img │ ├── grid.png │ ├── favicon.ico │ ├── glyphicons-halflings.png │ └── glyphicons-halflings-white.png ├── js │ └── theme.js ├── css │ └── prettify.css └── nav.html ├── .gitignore ├── MANIFEST.in ├── .tx └── config ├── requirements.txt ├── tox.ini ├── .travis.yml ├── LICENSE.md ├── runtests.py ├── setup.py └── mkdocs.yml /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rest_framework/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/browsable_api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rest_framework/authtoken/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rest_framework/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [wheel] 2 | universal = 1 3 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | www.django-rest-framework.org 2 | -------------------------------------------------------------------------------- /rest_framework/authtoken/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rest_framework/authtoken/south_migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rest_framework/models.py: -------------------------------------------------------------------------------- 1 | # Just to keep things like ./manage.py test happy 2 | -------------------------------------------------------------------------------- /docs/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/logo.png -------------------------------------------------------------------------------- /requirements/requirements-documentation.txt: -------------------------------------------------------------------------------- 1 | # MkDocs to build our documentation. 2 | mkdocs==0.11.1 3 | -------------------------------------------------------------------------------- /docs/img/apiary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/apiary.png -------------------------------------------------------------------------------- /docs/img/slate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/slate.png -------------------------------------------------------------------------------- /docs/img/cerulean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/cerulean.png -------------------------------------------------------------------------------- /docs/img/quickstart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/quickstart.png -------------------------------------------------------------------------------- /docs_theme/img/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs_theme/img/grid.png -------------------------------------------------------------------------------- /requirements/requirements-testing.txt: -------------------------------------------------------------------------------- 1 | # PyTest for running the tests. 2 | pytest==2.6.4 3 | pytest-django==2.8.0 4 | -------------------------------------------------------------------------------- /docs/img/sponsors/2-sga.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-sga.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-aba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-aba.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-isl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-isl.png -------------------------------------------------------------------------------- /docs/img/travis-status.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/travis-status.png -------------------------------------------------------------------------------- /docs_theme/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs_theme/img/favicon.ico -------------------------------------------------------------------------------- /docs_theme/js/theme.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | 3 | $('pre code').parent().addClass('prettyprint well'); 4 | 5 | }); 6 | -------------------------------------------------------------------------------- /docs/img/pages-pagination.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/pages-pagination.png -------------------------------------------------------------------------------- /docs/img/self-describing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/self-describing.png -------------------------------------------------------------------------------- /docs/img/sponsors/1-cyan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/1-cyan.png -------------------------------------------------------------------------------- /docs/img/sponsors/1-divio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/1-divio.png -------------------------------------------------------------------------------- /docs/img/sponsors/1-lulu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/1-lulu.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-byte.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-byte.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-crate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-crate.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-hipo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-hipo.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-vinta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-vinta.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-blimp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-blimp.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-garfo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-garfo.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-holvi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-holvi.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-tivix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-tivix.png -------------------------------------------------------------------------------- /requirements/requirements-codestyle.txt: -------------------------------------------------------------------------------- 1 | # PEP8 code linting, which we run on all commits. 2 | flake8==2.4.0 3 | pep8==1.5.7 4 | -------------------------------------------------------------------------------- /docs/img/cursor-pagination.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/cursor-pagination.png -------------------------------------------------------------------------------- /docs/img/django-rest-swagger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/django-rest-swagger.png -------------------------------------------------------------------------------- /docs/img/rest-framework-docs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/rest-framework-docs.png -------------------------------------------------------------------------------- /docs/img/sponsors/1-potato.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/1-potato.png -------------------------------------------------------------------------------- /docs/img/sponsors/1-runscope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/1-runscope.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-compile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-compile.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-cryptico.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-cryptico.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-django.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-django.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-heroku.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-heroku.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-hipflask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-hipflask.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-laterpay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-laterpay.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-nexthub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-nexthub.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-opbeat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-opbeat.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-rapasso.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-rapasso.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-sirono.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-sirono.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-wusawork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-wusawork.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-aditium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-aditium.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-beefarm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-beefarm.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-cantemo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-cantemo.gif -------------------------------------------------------------------------------- /docs/img/sponsors/3-gizmag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-gizmag.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-nephila.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-nephila.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-openeye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-openeye.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-phurba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-phurba.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-pkgfarm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-pkgfarm.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-safari.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-safari.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-shippo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-shippo.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-teonite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-teonite.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-vzzual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-vzzual.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-wildfish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-wildfish.png -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/list_fieldset.html: -------------------------------------------------------------------------------- 1 | Lists are not currently supported in HTML input. 2 | -------------------------------------------------------------------------------- /docs/img/labels-and-milestones.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/labels-and-milestones.png -------------------------------------------------------------------------------- /docs/img/sponsors/0-eventbrite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/0-eventbrite.png -------------------------------------------------------------------------------- /docs/img/sponsors/1-kuwaitnet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/1-kuwaitnet.png -------------------------------------------------------------------------------- /docs/img/sponsors/1-purplebit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/1-purplebit.png -------------------------------------------------------------------------------- /docs/img/sponsors/1-wiredrive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/1-wiredrive.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-prorenata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-prorenata.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-pulsecode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-pulsecode.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-alwaysdata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-alwaysdata.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-brightloop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-brightloop.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-fluxility.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-fluxility.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-ipushpull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-ipushpull.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-makespace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-makespace.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-pathwright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-pathwright.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-providenz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-providenz.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-trackmaven.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-trackmaven.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-transcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-transcode.png -------------------------------------------------------------------------------- /docs/img/link-header-pagination.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/link-header-pagination.png -------------------------------------------------------------------------------- /docs/img/sponsors/1-simple-energy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/1-simple-energy.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-koordinates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-koordinates.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-singing-horse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-singing-horse.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-ax_semantics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-ax_semantics.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-infinite_code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-infinite_code.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-life_the_game.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-life_the_game.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-lightning_kite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-lightning_kite.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-mirus_research.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-mirus_research.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-schuberg_philis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-schuberg_philis.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-thermondo-gmbh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-thermondo-gmbh.png -------------------------------------------------------------------------------- /docs_theme/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs_theme/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /docs/img/sponsors/1-vokal_interactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/1-vokal_interactive.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-rheinwerk_verlag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-rheinwerk_verlag.png -------------------------------------------------------------------------------- /docs/img/sponsors/2-security_compass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/2-security_compass.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-crosswordtracker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-crosswordtracker.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-triggered_messaging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-triggered_messaging.png -------------------------------------------------------------------------------- /docs/img/sponsors/3-imt_computer_services.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs/img/sponsors/3-imt_computer_services.png -------------------------------------------------------------------------------- /docs_theme/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/docs_theme/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /rest_framework/locale/ar/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/ar/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/cs/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/cs/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/da/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/da/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/de/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/de/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/en/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/en/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/es/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/es/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/et/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/et/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/fr/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/fr/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/hu/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/hu/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/id/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/id/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/it/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/it/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/mk/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/mk/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/nl/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/nl/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/pl/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/pl/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/ru/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/ru/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/sk/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/sk/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/sv/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/sv/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/tr/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/tr/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/uk/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/uk/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/vi/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/vi/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /tests/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | Blank URLConf just to keep the test suite happy 3 | """ 4 | from django.conf.urls import patterns 5 | 6 | urlpatterns = patterns('') 7 | -------------------------------------------------------------------------------- /rest_framework/locale/en_US/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/en_US/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/ko_KR/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/ko_KR/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/pt_BR/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/pt_BR/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/pt_PT/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/pt_PT/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/locale/zh_CN/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/locale/zh_CN/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/img/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/static/rest_framework/img/grid.png -------------------------------------------------------------------------------- /requirements/requirements-optionals.txt: -------------------------------------------------------------------------------- 1 | # Optional packages which may be used with REST framework. 2 | markdown==2.5.2 3 | django-guardian==1.2.5 4 | django-filter==0.9.2 5 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/api.html: -------------------------------------------------------------------------------- 1 | {% extends "rest_framework/base.html" %} 2 | 3 | {# Override this template in your own templates directory to customize #} 4 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/login.html: -------------------------------------------------------------------------------- 1 | {% extends "rest_framework/login_base.html" %} 2 | 3 | {# Override this template in your own templates directory to customize #} 4 | -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/static/rest_framework/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.db 3 | *~ 4 | .* 5 | 6 | /site/ 7 | /htmlcov/ 8 | /coverage/ 9 | /build/ 10 | /dist/ 11 | /*.egg-info/ 12 | /env/ 13 | MANIFEST 14 | 15 | !.gitignore 16 | !.travis.yml 17 | -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/static/rest_framework/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoralea/django-rest-framework/master/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /requirements/requirements-packaging.txt: -------------------------------------------------------------------------------- 1 | # Wheel for PyPI installs. 2 | wheel==0.24.0 3 | 4 | # Twine for secured PyPI uploads. 5 | twine==1.4.0 6 | 7 | # Transifex client for managing translation resources. 8 | transifex-client==0.10 9 | -------------------------------------------------------------------------------- /tests/browsable_api/no_auth_urls.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from django.conf.urls import patterns 3 | 4 | from .views import MockView 5 | 6 | urlpatterns = patterns( 7 | '', 8 | (r'^$', MockView.as_view()), 9 | ) 10 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/fieldset.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | {% for nested_field in field %} 3 | {% if not nested_field.read_only %} 4 | {% render_field nested_field style=style %} 5 | {% endif %} 6 | {% endfor %} 7 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include LICENSE.md 3 | recursive-include rest_framework/static *.js *.css *.png *.eot *.svg *.ttf *.woff 4 | recursive-include rest_framework/templates *.html 5 | recursive-exclude * __pycache__ 6 | recursive-exclude * *.py[co] 7 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/vertical/list_fieldset.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %}{{ field.label }}{% endif %} 3 |

Lists are not currently supported in HTML input.

4 |
5 | -------------------------------------------------------------------------------- /.tx/config: -------------------------------------------------------------------------------- 1 | [main] 2 | host = https://www.transifex.com 3 | 4 | [django-rest-framework.djangopo] 5 | file_filter = rest_framework/locale//LC_MESSAGES/django.po 6 | source_file = rest_framework/locale/en_US/LC_MESSAGES/django.po 7 | source_lang = en_US 8 | type = PO 9 | 10 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/api_form.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | {% csrf_token %} 3 | {% for field in form %} 4 | {% if not field.read_only %} 5 | {% render_field field style=style %} 6 | {% endif %} 7 | {% endfor %} 8 | 9 | -------------------------------------------------------------------------------- /rest_framework/authtoken/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from rest_framework.authtoken.models import Token 3 | 4 | 5 | class TokenAdmin(admin.ModelAdmin): 6 | list_display = ('key', 'user', 'created') 7 | fields = ('user',) 8 | ordering = ('-created',) 9 | 10 | 11 | admin.site.register(Token, TokenAdmin) 12 | -------------------------------------------------------------------------------- /tests/browsable_api/auth_urls.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from django.conf.urls import patterns, url, include 3 | 4 | from .views import MockView 5 | 6 | 7 | urlpatterns = patterns( 8 | '', 9 | (r'^$', MockView.as_view()), 10 | url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')), 11 | ) 12 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/checkbox.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 7 |
8 |
9 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/vertical/fieldset.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 |
3 | {% if field.label %}{{ field.label }}{% endif %} 4 | {% for nested_field in field %} 5 | {% if not nested_field.read_only %} 6 | {% render_field nested_field style=style %} 7 | {% endif %} 8 | {% endfor %} 9 |
10 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/textarea.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 4 | {% endif %} 5 | 6 |
7 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/vertical/form.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 |
3 | {% csrf_token %} 4 | {% for field in form %} 5 | {% if not field.read_only %} 6 | {% render_field field style=style %} 7 | {% endif %} 8 | {% endfor %} 9 | 10 | 11 |
12 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/form.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 |
3 | {% csrf_token %} 4 | {% for field in form %} 5 | {% if not field.read_only %} 6 | {% render_field field style=style %} 7 | {% endif %} 8 | {% endfor %} 9 | 10 | 11 |
12 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/raw_data_form.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 | {% csrf_token %} 3 | {{ form.non_field_errors }} 4 | {% for field in form %} 5 |
6 | {{ field.label_tag|add_class:"col-sm-2 control-label" }} 7 |
8 | {{ field|add_class:"form-control" }} 9 | {{ field.help_text }} 10 |
11 |
12 | {% endfor %} 13 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/input.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 4 | {% endif %} 5 | 6 |
7 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/pagination/previous_and_next.html: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /tests/browsable_api/views.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from rest_framework.views import APIView 4 | from rest_framework import authentication 5 | from rest_framework import renderers 6 | from rest_framework.response import Response 7 | 8 | 9 | class MockView(APIView): 10 | 11 | authentication_classes = (authentication.SessionAuthentication,) 12 | renderer_classes = (renderers.BrowsableAPIRenderer,) 13 | 14 | def get(self, request): 15 | return Response({'a': 1, 'b': 2, 'c': 3}) 16 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/checkbox_multiple.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 4 | {% endif %} 5 | {% for key, text in field.choices.items %} 6 |
7 | 11 |
12 | {% endfor %} 13 |
14 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/horizontal/fieldset.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 |
3 | {% if field.label %} 4 |
5 | {{ field.label }} 6 |
7 | {% endif %} 8 | {% for nested_field in field %} 9 | {% if not nested_field.read_only %} 10 | {% render_field nested_field style=style %} 11 | {% endif %} 12 | {% endfor %} 13 |
14 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/horizontal/form.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 |
3 | {% csrf_token %} 4 | {% for field in form %} 5 | {% if not field.read_only %} 6 | {% render_field field style=style %} 7 | {% endif %} 8 | {% endfor %} 9 | 10 |
11 |
12 | 13 |
14 |
15 |
16 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/horizontal/list_fieldset.html: -------------------------------------------------------------------------------- 1 | {% load rest_framework %} 2 |
3 | {% if field.label %} 4 |
5 | {{ field.label }} 6 |
7 | {% endif %} 8 | 15 |

Lists are not currently supported in HTML input.

16 |
17 | -------------------------------------------------------------------------------- /tests/test_settings.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from django.test import TestCase 3 | from rest_framework.settings import APISettings 4 | 5 | 6 | class TestSettings(TestCase): 7 | def test_import_error_message_maintained(self): 8 | """ 9 | Make sure import errors are captured and raised sensibly. 10 | """ 11 | settings = APISettings({ 12 | 'DEFAULT_RENDERER_CLASSES': [ 13 | 'tests.invalid_module.InvalidClassName' 14 | ] 15 | }) 16 | with self.assertRaises(ImportError): 17 | settings.DEFAULT_RENDERER_CLASSES 18 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # The base set of requirements for REST framework is actually 2 | # just Django, but for the purposes of development and testing 3 | # there are a number of packages that it is useful to install. 4 | 5 | # Laying these out as seperate requirements files, allows us to 6 | # only included the relevent sets when running tox, and ensures 7 | # we are only ever declaring out dependancies in one place. 8 | 9 | -r requirements/requirements-optionals.txt 10 | -r requirements/requirements-testing.txt 11 | -r requirements/requirements-documentation.txt 12 | -r requirements/requirements-codestyle.txt 13 | -r requirements/requirements-packaging.txt 14 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/vertical/checkbox.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 7 |
8 | {% if field.errors %} 9 | {% for error in field.errors %}{{ error }}{% endfor %} 10 | {% endif %} 11 | {% if field.help_text %} 12 | {{ field.help_text }} 13 | {% endif %} 14 |
-------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/select.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 4 | {% endif %} 5 | 13 |
14 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/select_multiple.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% trans "No items to select." as no_items %} 3 | 4 |
5 | {% if field.label %} 6 | 7 | {% endif %} 8 | 15 |
16 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/horizontal/checkbox.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 8 |
9 | {% if field.errors %} 10 | {% for error in field.errors %}{{ error }}{% endfor %} 11 | {% endif %} 12 | {% if field.help_text %} 13 | {{ field.help_text }} 14 | {% endif %} 15 |
16 |
17 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/vertical/textarea.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 4 | {% endif %} 5 | 6 | {% if field.errors %} 7 | {% for error in field.errors %}{{ error }}{% endfor %} 8 | {% endif %} 9 | {% if field.help_text %} 10 | {{ field.help_text }} 11 | {% endif %} 12 |
13 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/vertical/input.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 4 | {% endif %} 5 | 6 | {% if field.errors %} 7 | {% for error in field.errors %}{{ error }}{% endfor %} 8 | {% endif %} 9 | {% if field.help_text %} 10 | {{ field.help_text }} 11 | {% endif %} 12 |
13 | -------------------------------------------------------------------------------- /rest_framework/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | ______ _____ _____ _____ __ 3 | | ___ \ ___/ ___|_ _| / _| | | 4 | | |_/ / |__ \ `--. | | | |_ _ __ __ _ _ __ ___ _____ _____ _ __| |__ 5 | | /| __| `--. \ | | | _| '__/ _` | '_ ` _ \ / _ \ \ /\ / / _ \| '__| |/ / 6 | | |\ \| |___/\__/ / | | | | | | | (_| | | | | | | __/\ V V / (_) | | | < 7 | \_| \_\____/\____/ \_/ |_| |_| \__,_|_| |_| |_|\___| \_/\_/ \___/|_| |_|\_| 8 | """ 9 | 10 | __title__ = 'Django REST framework' 11 | __version__ = '3.1.1' 12 | __author__ = 'Tom Christie' 13 | __license__ = 'BSD 2-Clause' 14 | __copyright__ = 'Copyright 2011-2015 Tom Christie' 15 | 16 | # Version synonym 17 | VERSION = __version__ 18 | 19 | # Header encoding (see RFC5987) 20 | HTTP_HEADER_ENCODING = 'iso-8859-1' 21 | 22 | # Default datetime input and output formats 23 | ISO_8601 = 'iso-8601' 24 | -------------------------------------------------------------------------------- /rest_framework/authtoken/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Token', 17 | fields=[ 18 | ('key', models.CharField(primary_key=True, serialize=False, max_length=40)), 19 | ('created', models.DateTimeField(auto_now_add=True)), 20 | ('user', models.OneToOneField(to=settings.AUTH_USER_MODEL, related_name='auth_token')), 21 | ], 22 | options={ 23 | }, 24 | bases=(models.Model,), 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/horizontal/textarea.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 4 | {% endif %} 5 |
6 | 7 | {% if field.errors %} 8 | {% for error in field.errors %}{{ error }}{% endfor %} 9 | {% endif %} 10 | {% if field.help_text %} 11 | {{ field.help_text }} 12 | {% endif %} 13 |
14 |
15 | -------------------------------------------------------------------------------- /rest_framework/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | Login and logout views for the browsable API. 3 | 4 | Add these to your root URLconf if you're using the browsable API and 5 | your API requires authentication: 6 | 7 | urlpatterns = patterns('', 8 | ... 9 | url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')) 10 | ) 11 | 12 | The urls must be namespaced as 'rest_framework', and you should make sure 13 | your authentication settings include `SessionAuthentication`. 14 | """ 15 | from __future__ import unicode_literals 16 | from django.conf.urls import patterns, url 17 | from django.contrib.auth import views 18 | 19 | 20 | template_name = {'template_name': 'rest_framework/login.html'} 21 | 22 | urlpatterns = patterns( 23 | '', 24 | url(r'^login/$', views.login, template_name, name='login'), 25 | url(r'^logout/$', views.logout, template_name, name='logout') 26 | ) 27 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/horizontal/input.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 4 | {% endif %} 5 |
6 | 7 | {% if field.errors %} 8 | {% for error in field.errors %}{{ error }}{% endfor %} 9 | {% endif %} 10 | {% if field.help_text %} 11 | {{ field.help_text }} 12 | {% endif %} 13 |
14 |
15 | -------------------------------------------------------------------------------- /tests/description.py: -------------------------------------------------------------------------------- 1 | # -- coding: utf-8 -- 2 | 3 | # Apparently there is a python 2.6 issue where docstrings of imported view classes 4 | # do not retain their encoding information even if a module has a proper 5 | # encoding declaration at the top of its source file. Therefore for tests 6 | # to catch unicode related errors, a mock view has to be declared in a separate 7 | # module. 8 | 9 | from rest_framework.views import APIView 10 | 11 | 12 | # test strings snatched from http://www.columbia.edu/~fdc/utf8/, 13 | # http://winrus.com/utf8-jap.htm and memory 14 | UTF8_TEST_DOCSTRING = ( 15 | 'zażółć gęślą jaźń' 16 | 'Sîne klâwen durh die wolken sint geslagen' 17 | 'Τη γλώσσα μου έδωσαν ελληνική' 18 | 'யாமறிந்த மொழிகளிலே தமிழ்மொழி' 19 | 'На берегу пустынных волн' 20 | 'てすと' 21 | 'アイウエオカキクケコサシスセソタチツテ' 22 | ) 23 | 24 | 25 | class ViewWithNonASCIICharactersInDocstring(APIView): 26 | __doc__ = UTF8_TEST_DOCSTRING 27 | -------------------------------------------------------------------------------- /docs_theme/css/prettify.css: -------------------------------------------------------------------------------- 1 | .com { color: #93a1a1; } 2 | .lit { color: #195f91; } 3 | .pun, .opn, .clo { color: #93a1a1; } 4 | .fun { color: #dc322f; } 5 | .str, .atv { color: #D14; } 6 | .kwd, .prettyprint .tag { color: #1e347b; } 7 | .typ, .atn, .dec, .var { color: teal; } 8 | .pln { color: #48484c; } 9 | 10 | .prettyprint { 11 | padding: 8px; 12 | background-color: #f7f7f9; 13 | border: 1px solid #e1e1e8; 14 | } 15 | .prettyprint.linenums { 16 | -webkit-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 17 | -moz-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 18 | box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 19 | } 20 | 21 | /* Specify class=linenums on a pre to get line numbering */ 22 | ol.linenums { 23 | margin: 0 0 0 33px; /* IE indents via margin-left */ 24 | } 25 | ol.linenums li { 26 | padding-left: 12px; 27 | color: #bebec5; 28 | line-height: 20px; 29 | text-shadow: 0 1px 0 #fff; 30 | } -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/inline/radio.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% trans "None" as none_choice %} 3 | 4 |
5 | {% if field.label %} 6 | 7 | {% endif %} 8 | {% if field.allow_null or field.allow_blank %} 9 |
10 | 14 |
15 | {% endif %} 16 | {% for key, text in field.choices.items %} 17 |
18 | 22 |
23 | {% endfor %} 24 |
25 | -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/css/prettify.css: -------------------------------------------------------------------------------- 1 | .com { color: #93a1a1; } 2 | .lit { color: #195f91; } 3 | .pun, .opn, .clo { color: #93a1a1; } 4 | .fun { color: #dc322f; } 5 | .str, .atv { color: #D14; } 6 | .kwd, .prettyprint .tag { color: #1e347b; } 7 | .typ, .atn, .dec, .var { color: teal; } 8 | .pln { color: #48484c; } 9 | 10 | .prettyprint { 11 | padding: 8px; 12 | background-color: #f7f7f9; 13 | border: 1px solid #e1e1e8; 14 | } 15 | .prettyprint.linenums { 16 | -webkit-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 17 | -moz-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 18 | box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0; 19 | } 20 | 21 | /* Specify class=linenums on a pre to get line numbering */ 22 | ol.linenums { 23 | margin: 0 0 0 33px; /* IE indents via margin-left */ 24 | } 25 | ol.linenums li { 26 | padding-left: 12px; 27 | color: #bebec5; 28 | line-height: 20px; 29 | text-shadow: 0 1px 0 #fff; 30 | } -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/vertical/select.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 4 | {% endif %} 5 | 13 | {% if field.errors %} 14 | {% for error in field.errors %}{{ error }}{% endfor %} 15 | {% endif %} 16 | {% if field.help_text %} 17 | {{ field.help_text }} 18 | {% endif %} 19 |
20 | -------------------------------------------------------------------------------- /rest_framework/authtoken/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework.views import APIView 2 | from rest_framework import parsers 3 | from rest_framework import renderers 4 | from rest_framework.response import Response 5 | from rest_framework.authtoken.models import Token 6 | from rest_framework.authtoken.serializers import AuthTokenSerializer 7 | 8 | 9 | class ObtainAuthToken(APIView): 10 | throttle_classes = () 11 | permission_classes = () 12 | parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,) 13 | renderer_classes = (renderers.JSONRenderer,) 14 | serializer_class = AuthTokenSerializer 15 | 16 | def post(self, request): 17 | serializer = self.serializer_class(data=request.data) 18 | serializer.is_valid(raise_exception=True) 19 | user = serializer.validated_data['user'] 20 | token, created = Token.objects.get_or_create(user=user) 21 | return Response({'token': token.key}) 22 | 23 | 24 | obtain_auth_token = ObtainAuthToken.as_view() 25 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/vertical/select_multiple.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% trans "No items to select." as no_items %} 3 | 4 |
5 | {% if field.label %} 6 | 7 | {% endif %} 8 | 15 | {% if field.errors %} 16 | {% for error in field.errors %}{{ error }}{% endfor %} 17 | {% endif %} 18 | {% if field.help_text %} 19 | {{ field.help_text }} 20 | {% endif %} 21 |
22 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/horizontal/select.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 4 | {% endif %} 5 |
6 | 14 | {% if field.errors %} 15 | {% for error in field.errors %}{{ error }}{% endfor %} 16 | {% endif %} 17 | {% if field.help_text %} 18 | {{ field.help_text }} 19 | {% endif %} 20 |
21 |
22 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/horizontal/select_multiple.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% trans "No items to select." as no_items %} 3 | 4 |
5 | {% if field.label %} 6 | 7 | {% endif %} 8 |
9 | 16 | {% if field.errors %} 17 | {% for error in field.errors %}{{ error }}{% endfor %} 18 | {% endif %} 19 | {% if field.help_text %} 20 | {{ field.help_text }} 21 | {% endif %} 22 |
23 |
24 | -------------------------------------------------------------------------------- /rest_framework/utils/urls.py: -------------------------------------------------------------------------------- 1 | from django.utils.six.moves.urllib import parse as urlparse 2 | 3 | 4 | def replace_query_param(url, key, val): 5 | """ 6 | Given a URL and a key/val pair, set or replace an item in the query 7 | parameters of the URL, and return the new URL. 8 | """ 9 | (scheme, netloc, path, query, fragment) = urlparse.urlsplit(url) 10 | query_dict = urlparse.parse_qs(query) 11 | query_dict[key] = [val] 12 | query = urlparse.urlencode(sorted(list(query_dict.items())), doseq=True) 13 | return urlparse.urlunsplit((scheme, netloc, path, query, fragment)) 14 | 15 | 16 | def remove_query_param(url, key): 17 | """ 18 | Given a URL and a key/val pair, remove an item in the query 19 | parameters of the URL, and return the new URL. 20 | """ 21 | (scheme, netloc, path, query, fragment) = urlparse.urlsplit(url) 22 | query_dict = urlparse.parse_qs(query) 23 | query_dict.pop(key, None) 24 | query = urlparse.urlencode(sorted(list(query_dict.items())), doseq=True) 25 | return urlparse.urlunsplit((scheme, netloc, path, query, fragment)) 26 | -------------------------------------------------------------------------------- /tests/test_write_only_fields.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from rest_framework import serializers 3 | 4 | 5 | class WriteOnlyFieldTests(TestCase): 6 | def setUp(self): 7 | class ExampleSerializer(serializers.Serializer): 8 | email = serializers.EmailField() 9 | password = serializers.CharField(write_only=True) 10 | 11 | def create(self, attrs): 12 | return attrs 13 | 14 | self.Serializer = ExampleSerializer 15 | 16 | def write_only_fields_are_present_on_input(self): 17 | data = { 18 | 'email': 'foo@example.com', 19 | 'password': '123' 20 | } 21 | serializer = self.Serializer(data=data) 22 | self.assertTrue(serializer.is_valid()) 23 | self.assertEquals(serializer.validated_data, data) 24 | 25 | def write_only_fields_are_not_present_on_output(self): 26 | instance = { 27 | 'email': 'foo@example.com', 28 | 'password': '123' 29 | } 30 | serializer = self.Serializer(instance) 31 | self.assertEquals(serializer.data, {'email': 'foo@example.com'}) 32 | -------------------------------------------------------------------------------- /rest_framework/authtoken/serializers.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import authenticate 2 | from django.utils.translation import ugettext_lazy as _ 3 | 4 | from rest_framework import exceptions, serializers 5 | 6 | 7 | class AuthTokenSerializer(serializers.Serializer): 8 | username = serializers.CharField() 9 | password = serializers.CharField(style={'input_type': 'password'}) 10 | 11 | def validate(self, attrs): 12 | username = attrs.get('username') 13 | password = attrs.get('password') 14 | 15 | if username and password: 16 | user = authenticate(username=username, password=password) 17 | 18 | if user: 19 | if not user.is_active: 20 | msg = _('User account is disabled.') 21 | raise exceptions.ValidationError(msg) 22 | else: 23 | msg = _('Unable to log in with provided credentials.') 24 | raise exceptions.ValidationError(msg) 25 | else: 26 | msg = _('Must include "username" and "password".') 27 | raise exceptions.ValidationError(msg) 28 | 29 | attrs['user'] = user 30 | return attrs 31 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | py27-{flake8,docs}, 4 | {py26,py27}-django14, 5 | {py26,py27,py32,py33,py34}-django{15,16}, 6 | {py27,py32,py33,py34}-django{17,18,master} 7 | 8 | [testenv] 9 | commands = ./runtests.py --fast 10 | setenv = 11 | PYTHONDONTWRITEBYTECODE=1 12 | deps = 13 | django14: Django==1.4.11 # Should track minimum supported 14 | django15: Django==1.5.6 # Should track minimum supported 15 | django16: Django==1.6.3 # Should track minimum supported 16 | django17: Django==1.7.2 # Should track maximum supported 17 | django18: Django==1.8 # Should track maximum supported 18 | djangomaster: https://github.com/django/django/archive/master.tar.gz 19 | -rrequirements/requirements-testing.txt 20 | -rrequirements/requirements-optionals.txt 21 | 22 | [testenv:py27-flake8] 23 | deps = 24 | -rrequirements/requirements-codestyle.txt 25 | -rrequirements/requirements-testing.txt 26 | commands = ./runtests.py --lintonly 27 | 28 | [testenv:py27-docs] 29 | deps = 30 | -rrequirements/requirements-testing.txt 31 | -rrequirements/requirements-documentation.txt 32 | commands = mkdocs build 33 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | sudo: false 4 | 5 | env: 6 | - TOX_ENV=py27-flake8 7 | - TOX_ENV=py27-docs 8 | - TOX_ENV=py34-django18 9 | - TOX_ENV=py33-django18 10 | - TOX_ENV=py32-django18 11 | - TOX_ENV=py27-django18 12 | - TOX_ENV=py34-django17 13 | - TOX_ENV=py33-django17 14 | - TOX_ENV=py32-django17 15 | - TOX_ENV=py27-django17 16 | - TOX_ENV=py34-django16 17 | - TOX_ENV=py33-django16 18 | - TOX_ENV=py32-django16 19 | - TOX_ENV=py27-django16 20 | - TOX_ENV=py26-django16 21 | - TOX_ENV=py34-django15 22 | - TOX_ENV=py33-django15 23 | - TOX_ENV=py32-django15 24 | - TOX_ENV=py27-django15 25 | - TOX_ENV=py26-django15 26 | - TOX_ENV=py27-django14 27 | - TOX_ENV=py26-django14 28 | - TOX_ENV=py27-djangomaster 29 | - TOX_ENV=py32-djangomaster 30 | - TOX_ENV=py33-djangomaster 31 | - TOX_ENV=py34-djangomaster 32 | 33 | matrix: 34 | fast_finish: true 35 | allow_failures: 36 | - env: TOX_ENV=py27-djangomaster 37 | - env: TOX_ENV=py32-djangomaster 38 | - env: TOX_ENV=py33-djangomaster 39 | - env: TOX_ENV=py34-djangomaster 40 | 41 | install: 42 | - pip install tox 43 | 44 | script: 45 | - tox -e $TOX_ENV 46 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/pagination/numbers.html: -------------------------------------------------------------------------------- 1 | 28 | -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/css/default.css: -------------------------------------------------------------------------------- 1 | 2 | /* The navbar is fixed at >= 980px wide, so add padding to the body to prevent 3 | content running up underneath it. */ 4 | 5 | h1 { 6 | font-weight: 500; 7 | } 8 | 9 | h2, h3 { 10 | font-weight: 300; 11 | } 12 | 13 | .resource-description, .response-info { 14 | margin-bottom: 2em; 15 | } 16 | 17 | .version:before { 18 | content: "v"; 19 | opacity: 0.6; 20 | padding-right: 0.25em; 21 | } 22 | 23 | .version { 24 | font-size: 70%; 25 | } 26 | 27 | .format-option { 28 | font-family: Menlo, Consolas, "Andale Mono", "Lucida Console", monospace; 29 | } 30 | 31 | .button-form { 32 | float: right; 33 | margin-right: 1em; 34 | } 35 | 36 | form select, form input, form textarea { 37 | width: 90%; 38 | } 39 | 40 | form select[multiple] { 41 | height: 150px; 42 | } 43 | 44 | /* To allow tooltips to work on disabled elements */ 45 | .disabled-tooltip-shield { 46 | position: absolute; 47 | top: 0; 48 | right: 0; 49 | bottom: 0; 50 | left: 0; 51 | } 52 | 53 | .errorlist { 54 | margin-top: 0.5em; 55 | } 56 | 57 | pre { 58 | overflow: auto; 59 | word-wrap: normal; 60 | white-space: pre; 61 | font-size: 12px; 62 | } 63 | 64 | .page-header { 65 | border-bottom: none; 66 | padding-bottom: 0px; 67 | } 68 | -------------------------------------------------------------------------------- /tests/test_serializer_nested.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | 4 | class TestNestedSerializer: 5 | def setup(self): 6 | class NestedSerializer(serializers.Serializer): 7 | one = serializers.IntegerField(max_value=10) 8 | two = serializers.IntegerField(max_value=10) 9 | 10 | class TestSerializer(serializers.Serializer): 11 | nested = NestedSerializer() 12 | 13 | self.Serializer = TestSerializer 14 | 15 | def test_nested_validate(self): 16 | input_data = { 17 | 'nested': { 18 | 'one': '1', 19 | 'two': '2', 20 | } 21 | } 22 | expected_data = { 23 | 'nested': { 24 | 'one': 1, 25 | 'two': 2, 26 | } 27 | } 28 | serializer = self.Serializer(data=input_data) 29 | assert serializer.is_valid() 30 | assert serializer.validated_data == expected_data 31 | 32 | def test_nested_serialize_empty(self): 33 | expected_data = { 34 | 'nested': { 35 | 'one': None, 36 | 'two': None 37 | } 38 | } 39 | serializer = self.Serializer() 40 | assert serializer.data == expected_data 41 | -------------------------------------------------------------------------------- /tests/test_status.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from django.test import TestCase 3 | from rest_framework.status import ( 4 | is_informational, is_success, is_redirect, is_client_error, is_server_error 5 | ) 6 | 7 | 8 | class TestStatus(TestCase): 9 | def test_status_categories(self): 10 | self.assertFalse(is_informational(99)) 11 | self.assertTrue(is_informational(100)) 12 | self.assertTrue(is_informational(199)) 13 | self.assertFalse(is_informational(200)) 14 | 15 | self.assertFalse(is_success(199)) 16 | self.assertTrue(is_success(200)) 17 | self.assertTrue(is_success(299)) 18 | self.assertFalse(is_success(300)) 19 | 20 | self.assertFalse(is_redirect(299)) 21 | self.assertTrue(is_redirect(300)) 22 | self.assertTrue(is_redirect(399)) 23 | self.assertFalse(is_redirect(400)) 24 | 25 | self.assertFalse(is_client_error(399)) 26 | self.assertTrue(is_client_error(400)) 27 | self.assertTrue(is_client_error(499)) 28 | self.assertFalse(is_client_error(500)) 29 | 30 | self.assertFalse(is_server_error(499)) 31 | self.assertTrue(is_server_error(500)) 32 | self.assertTrue(is_server_error(599)) 33 | self.assertFalse(is_server_error(600)) 34 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/vertical/checkbox_multiple.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 4 | {% endif %} 5 | {% if style.inline %} 6 |
7 | {% for key, text in field.choices.items %} 8 | 12 | {% endfor %} 13 |
14 | {% else %} 15 | {% for key, text in field.choices.items %} 16 |
17 | 21 |
22 | {% endfor %} 23 | {% endif %} 24 | {% if field.errors %} 25 | {% for error in field.errors %}{{ error }}{% endfor %} 26 | {% endif %} 27 | {% if field.help_text %} 28 | {{ field.help_text }} 29 | {% endif %} 30 |
31 | -------------------------------------------------------------------------------- /tests/test_middleware.py: -------------------------------------------------------------------------------- 1 | 2 | from django.conf.urls import patterns, url 3 | from django.contrib.auth.models import User 4 | from rest_framework.authentication import TokenAuthentication 5 | from rest_framework.authtoken.models import Token 6 | from rest_framework.test import APITestCase 7 | from rest_framework.views import APIView 8 | 9 | 10 | urlpatterns = patterns( 11 | '', 12 | url(r'^$', APIView.as_view(authentication_classes=(TokenAuthentication,))), 13 | ) 14 | 15 | 16 | class MyMiddleware(object): 17 | 18 | def process_response(self, request, response): 19 | assert hasattr(request, 'user'), '`user` is not set on request' 20 | assert request.user.is_authenticated(), '`user` is not authenticated' 21 | return response 22 | 23 | 24 | class TestMiddleware(APITestCase): 25 | 26 | urls = 'tests.test_middleware' 27 | 28 | def test_middleware_can_access_user_when_processing_response(self): 29 | user = User.objects.create_user('john', 'john@example.com', 'password') 30 | key = 'abcd1234' 31 | Token.objects.create(key=key, user=user) 32 | 33 | with self.settings( 34 | MIDDLEWARE_CLASSES=('tests.test_middleware.MyMiddleware',) 35 | ): 36 | auth = 'Token ' + key 37 | self.client.get('/', HTTP_AUTHORIZATION=auth) 38 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/horizontal/checkbox_multiple.html: -------------------------------------------------------------------------------- 1 |
2 | {% if field.label %} 3 | 4 | {% endif %} 5 |
6 | {% if style.inline %} 7 | {% for key, text in field.choices.items %} 8 | 12 | {% endfor %} 13 | {% else %} 14 | {% for key, text in field.choices.items %} 15 |
16 | 20 |
21 | {% endfor %} 22 | {% endif %} 23 | {% if field.errors %} 24 | {% for error in field.errors %}{{ error }}{% endfor %} 25 | {% endif %} 26 | {% if field.help_text %} 27 | {{ field.help_text }} 28 | {% endif %} 29 |
30 |
31 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | Copyright (c) 2011-2015, Tom Christie 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | Redistributions in binary form must reproduce the above copyright notice, this 12 | list of conditions and the following disclaimer in the documentation and/or 13 | other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /tests/test_viewsets.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from rest_framework import status 3 | from rest_framework.response import Response 4 | from rest_framework.test import APIRequestFactory 5 | from rest_framework.viewsets import GenericViewSet 6 | 7 | 8 | factory = APIRequestFactory() 9 | 10 | 11 | class BasicViewSet(GenericViewSet): 12 | def list(self, request, *args, **kwargs): 13 | return Response({'ACTION': 'LIST'}) 14 | 15 | 16 | class InitializeViewSetsTestCase(TestCase): 17 | def test_initialize_view_set_with_actions(self): 18 | request = factory.get('/', '', content_type='application/json') 19 | my_view = BasicViewSet.as_view(actions={ 20 | 'get': 'list', 21 | }) 22 | 23 | response = my_view(request) 24 | self.assertEqual(response.status_code, status.HTTP_200_OK) 25 | self.assertEqual(response.data, {'ACTION': 'LIST'}) 26 | 27 | def test_initialize_view_set_with_empty_actions(self): 28 | try: 29 | BasicViewSet.as_view() 30 | except TypeError as e: 31 | self.assertEqual(str(e), "The `actions` argument must be provided " 32 | "when calling `.as_view()` on a ViewSet. " 33 | "For example `.as_view({'get': 'list'})`") 34 | else: 35 | self.fail("actions must not be empty.") 36 | -------------------------------------------------------------------------------- /rest_framework/utils/humanize_datetime.py: -------------------------------------------------------------------------------- 1 | """ 2 | Helper functions that convert strftime formats into more readable representations. 3 | """ 4 | from rest_framework import ISO_8601 5 | 6 | 7 | def datetime_formats(formats): 8 | format = ', '.join(formats).replace( 9 | ISO_8601, 10 | 'YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]' 11 | ) 12 | return humanize_strptime(format) 13 | 14 | 15 | def date_formats(formats): 16 | format = ', '.join(formats).replace(ISO_8601, 'YYYY[-MM[-DD]]') 17 | return humanize_strptime(format) 18 | 19 | 20 | def time_formats(formats): 21 | format = ', '.join(formats).replace(ISO_8601, 'hh:mm[:ss[.uuuuuu]]') 22 | return humanize_strptime(format) 23 | 24 | 25 | def humanize_strptime(format_string): 26 | # Note that we're missing some of the locale specific mappings that 27 | # don't really make sense. 28 | mapping = { 29 | "%Y": "YYYY", 30 | "%y": "YY", 31 | "%m": "MM", 32 | "%b": "[Jan-Dec]", 33 | "%B": "[January-December]", 34 | "%d": "DD", 35 | "%H": "hh", 36 | "%I": "hh", # Requires '%p' to differentiate from '%H'. 37 | "%M": "mm", 38 | "%S": "ss", 39 | "%f": "uuuuuu", 40 | "%a": "[Mon-Sun]", 41 | "%A": "[Monday-Sunday]", 42 | "%p": "[AM|PM]", 43 | "%z": "[+HHMM|-HHMM]" 44 | } 45 | for key, val in mapping.items(): 46 | format_string = format_string.replace(key, val) 47 | return format_string 48 | -------------------------------------------------------------------------------- /rest_framework/authtoken/models.py: -------------------------------------------------------------------------------- 1 | import binascii 2 | import os 3 | 4 | from django.conf import settings 5 | from django.db import models 6 | from django.utils.encoding import python_2_unicode_compatible 7 | 8 | 9 | # Prior to Django 1.5, the AUTH_USER_MODEL setting does not exist. 10 | # Note that we don't perform this code in the compat module due to 11 | # bug report #1297 12 | # See: https://github.com/tomchristie/django-rest-framework/issues/1297 13 | AUTH_USER_MODEL = getattr(settings, 'AUTH_USER_MODEL', 'auth.User') 14 | 15 | 16 | @python_2_unicode_compatible 17 | class Token(models.Model): 18 | """ 19 | The default authorization token model. 20 | """ 21 | key = models.CharField(max_length=40, primary_key=True) 22 | user = models.OneToOneField(AUTH_USER_MODEL, related_name='auth_token') 23 | created = models.DateTimeField(auto_now_add=True) 24 | 25 | class Meta: 26 | # Work around for a bug in Django: 27 | # https://code.djangoproject.com/ticket/19422 28 | # 29 | # Also see corresponding ticket: 30 | # https://github.com/tomchristie/django-rest-framework/issues/705 31 | abstract = 'rest_framework.authtoken' not in settings.INSTALLED_APPS 32 | 33 | def save(self, *args, **kwargs): 34 | if not self.key: 35 | self.key = self.generate_key() 36 | return super(Token, self).save(*args, **kwargs) 37 | 38 | def generate_key(self): 39 | return binascii.hexlify(os.urandom(20)).decode() 40 | 41 | def __str__(self): 42 | return self.key 43 | -------------------------------------------------------------------------------- /rest_framework/reverse.py: -------------------------------------------------------------------------------- 1 | """ 2 | Provide urlresolver functions that return fully qualified URLs or view names 3 | """ 4 | from __future__ import unicode_literals 5 | from django.core.urlresolvers import reverse as django_reverse 6 | from django.core.urlresolvers import NoReverseMatch 7 | from django.utils import six 8 | from django.utils.functional import lazy 9 | 10 | 11 | def reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra): 12 | """ 13 | If versioning is being used then we pass any `reverse` calls through 14 | to the versioning scheme instance, so that the resulting URL 15 | can be modified if needed. 16 | """ 17 | scheme = getattr(request, 'versioning_scheme', None) 18 | if scheme is not None: 19 | try: 20 | return scheme.reverse(viewname, args, kwargs, request, format, **extra) 21 | except NoReverseMatch: 22 | # In case the versioning scheme reversal fails, fallback to the 23 | # default implementation 24 | pass 25 | 26 | return _reverse(viewname, args, kwargs, request, format, **extra) 27 | 28 | 29 | def _reverse(viewname, args=None, kwargs=None, request=None, format=None, **extra): 30 | """ 31 | Same as `django.core.urlresolvers.reverse`, but optionally takes a request 32 | and returns a fully qualified URL, using the request to get the base URL. 33 | """ 34 | if format is not None: 35 | kwargs = kwargs or {} 36 | kwargs['format'] = format 37 | url = django_reverse(viewname, args=args, kwargs=kwargs, **extra) 38 | if request: 39 | return request.build_absolute_uri(url) 40 | return url 41 | 42 | 43 | reverse_lazy = lazy(reverse, six.text_type) 44 | -------------------------------------------------------------------------------- /tests/test_reverse.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from django.conf.urls import patterns, url 3 | from django.core.urlresolvers import NoReverseMatch 4 | from django.test import TestCase 5 | from rest_framework.reverse import reverse 6 | from rest_framework.test import APIRequestFactory 7 | 8 | factory = APIRequestFactory() 9 | 10 | 11 | def null_view(request): 12 | pass 13 | 14 | urlpatterns = patterns( 15 | '', 16 | url(r'^view$', null_view, name='view'), 17 | ) 18 | 19 | 20 | class MockVersioningScheme(object): 21 | 22 | def __init__(self, raise_error=False): 23 | self.raise_error = raise_error 24 | 25 | def reverse(self, *args, **kwargs): 26 | if self.raise_error: 27 | raise NoReverseMatch() 28 | 29 | return 'http://scheme-reversed/view' 30 | 31 | 32 | class ReverseTests(TestCase): 33 | """ 34 | Tests for fully qualified URLs when using `reverse`. 35 | """ 36 | urls = 'tests.test_reverse' 37 | 38 | def test_reversed_urls_are_fully_qualified(self): 39 | request = factory.get('/view') 40 | url = reverse('view', request=request) 41 | self.assertEqual(url, 'http://testserver/view') 42 | 43 | def test_reverse_with_versioning_scheme(self): 44 | request = factory.get('/view') 45 | request.versioning_scheme = MockVersioningScheme() 46 | 47 | url = reverse('view', request=request) 48 | self.assertEqual(url, 'http://scheme-reversed/view') 49 | 50 | def test_reverse_with_versioning_scheme_fallback_to_default_on_error(self): 51 | request = factory.get('/view') 52 | request.versioning_scheme = MockVersioningScheme(raise_error=True) 53 | 54 | url = reverse('view', request=request) 55 | self.assertEqual(url, 'http://testserver/view') 56 | -------------------------------------------------------------------------------- /tests/test_negotiation.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from django.test import TestCase 3 | from rest_framework.negotiation import DefaultContentNegotiation 4 | from rest_framework.request import Request 5 | from rest_framework.renderers import BaseRenderer 6 | from rest_framework.test import APIRequestFactory 7 | 8 | 9 | factory = APIRequestFactory() 10 | 11 | 12 | class MockJSONRenderer(BaseRenderer): 13 | media_type = 'application/json' 14 | 15 | 16 | class MockHTMLRenderer(BaseRenderer): 17 | media_type = 'text/html' 18 | 19 | 20 | class NoCharsetSpecifiedRenderer(BaseRenderer): 21 | media_type = 'my/media' 22 | 23 | 24 | class TestAcceptedMediaType(TestCase): 25 | def setUp(self): 26 | self.renderers = [MockJSONRenderer(), MockHTMLRenderer()] 27 | self.negotiator = DefaultContentNegotiation() 28 | 29 | def select_renderer(self, request): 30 | return self.negotiator.select_renderer(request, self.renderers) 31 | 32 | def test_client_without_accept_use_renderer(self): 33 | request = Request(factory.get('/')) 34 | accepted_renderer, accepted_media_type = self.select_renderer(request) 35 | self.assertEqual(accepted_media_type, 'application/json') 36 | 37 | def test_client_underspecifies_accept_use_renderer(self): 38 | request = Request(factory.get('/', HTTP_ACCEPT='*/*')) 39 | accepted_renderer, accepted_media_type = self.select_renderer(request) 40 | self.assertEqual(accepted_media_type, 'application/json') 41 | 42 | def test_client_overspecifies_accept_use_client(self): 43 | request = Request(factory.get('/', HTTP_ACCEPT='application/json; indent=8')) 44 | accepted_renderer, accepted_media_type = self.select_renderer(request) 45 | self.assertEqual(accepted_media_type, 'application/json; indent=8') 46 | -------------------------------------------------------------------------------- /docs/topics/writable-nested-serializers.md: -------------------------------------------------------------------------------- 1 | > To save HTTP requests, it may be convenient to send related documents along with the request. 2 | > 3 | > — [JSON API specification for Ember Data][cite]. 4 | 5 | # Writable nested serializers 6 | 7 | Although flat data structures serve to properly delineate between the individual entities in your service, there are cases where it may be more appropriate or convenient to use nested data structures. 8 | 9 | Nested data structures are easy enough to work with if they're read-only - simply nest your serializer classes and you're good to go. However, there are a few more subtleties to using writable nested serializers, due to the dependencies between the various model instances, and the need to save or delete multiple instances in a single action. 10 | 11 | ## One-to-many data structures 12 | 13 | *Example of a **read-only** nested serializer. Nothing complex to worry about here.* 14 | 15 | class ToDoItemSerializer(serializers.ModelSerializer): 16 | class Meta: 17 | model = ToDoItem 18 | fields = ('text', 'is_completed') 19 | 20 | class ToDoListSerializer(serializers.ModelSerializer): 21 | items = ToDoItemSerializer(many=True, read_only=True) 22 | 23 | class Meta: 24 | model = ToDoList 25 | fields = ('title', 'items') 26 | 27 | Some example output from our serializer. 28 | 29 | { 30 | 'title': 'Leaving party preperations', 31 | 'items': [ 32 | {'text': 'Compile playlist', 'is_completed': True}, 33 | {'text': 'Send invites', 'is_completed': False}, 34 | {'text': 'Clean house', 'is_completed': False} 35 | ] 36 | } 37 | 38 | Let's take a look at updating our nested one-to-many data structure. 39 | 40 | ### Validation errors 41 | 42 | ### Adding and removing items 43 | 44 | ### Making PATCH requests 45 | 46 | 47 | [cite]: http://jsonapi.org/format/#url-based-json-api 48 | -------------------------------------------------------------------------------- /rest_framework/static/rest_framework/js/default.js: -------------------------------------------------------------------------------- 1 | function getCookie(c_name) 2 | { 3 | // From http://www.w3schools.com/js/js_cookies.asp 4 | var c_value = document.cookie; 5 | var c_start = c_value.indexOf(" " + c_name + "="); 6 | if (c_start == -1) { 7 | c_start = c_value.indexOf(c_name + "="); 8 | } 9 | if (c_start == -1) { 10 | c_value = null; 11 | } else { 12 | c_start = c_value.indexOf("=", c_start) + 1; 13 | var c_end = c_value.indexOf(";", c_start); 14 | if (c_end == -1) { 15 | c_end = c_value.length; 16 | } 17 | c_value = unescape(c_value.substring(c_start,c_end)); 18 | } 19 | return c_value; 20 | } 21 | 22 | // JSON highlighting. 23 | prettyPrint(); 24 | 25 | // Bootstrap tooltips. 26 | $('.js-tooltip').tooltip({ 27 | delay: 1000, 28 | container: 'body' 29 | }); 30 | 31 | // Deal with rounded tab styling after tab clicks. 32 | $('a[data-toggle="tab"]:first').on('shown', function (e) { 33 | $(e.target).parents('.tabbable').addClass('first-tab-active'); 34 | }); 35 | $('a[data-toggle="tab"]:not(:first)').on('shown', function (e) { 36 | $(e.target).parents('.tabbable').removeClass('first-tab-active'); 37 | }); 38 | 39 | $('a[data-toggle="tab"]').click(function(){ 40 | document.cookie="tabstyle=" + this.name + "; path=/"; 41 | }); 42 | 43 | // Store tab preference in cookies & display appropriate tab on load. 44 | var selectedTab = null; 45 | var selectedTabName = getCookie('tabstyle'); 46 | 47 | if (selectedTabName) { 48 | selectedTabName = selectedTabName.replace(/[^a-z-]/g, ''); 49 | } 50 | 51 | if (selectedTabName) { 52 | selectedTab = $('.form-switcher a[name=' + selectedTabName + ']'); 53 | } 54 | 55 | if (selectedTab && selectedTab.length > 0) { 56 | // Display whichever tab is selected. 57 | selectedTab.tab('show'); 58 | } else { 59 | // If no tab selected, display rightmost tab. 60 | $('.form-switcher a:first').tab('show'); 61 | } 62 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/horizontal/radio.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% trans "None" as none_choice %} 3 | 4 |
5 | {% if field.label %} 6 | 7 | {% endif %} 8 |
9 | {% if style.inline %} 10 | {% if field.allow_null or field.allow_blank %} 11 | 15 | {% endif %} 16 | {% for key, text in field.choices.items %} 17 | 21 | {% endfor %} 22 | {% else %} 23 | {% if field.allow_null or field.allow_blank %} 24 |
25 | 29 |
30 | {% endif %} 31 | {% for key, text in field.choices.items %} 32 |
33 | 37 |
38 | {% endfor %} 39 | {% endif %} 40 | {% if field.errors %} 41 | {% for error in field.errors %}{{ error }}{% endfor %} 42 | {% endif %} 43 | {% if field.help_text %} 44 | {{ field.help_text }} 45 | {% endif %} 46 |
47 |
48 | -------------------------------------------------------------------------------- /rest_framework/templates/rest_framework/vertical/radio.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% trans "None" as none_choice %} 3 | 4 |
5 | {% if field.label %} 6 | 7 | {% endif %} 8 | {% if style.inline %} 9 |
10 | {% if field.allow_null or field.allow_blank %} 11 | 15 | {% endif %} 16 | {% for key, text in field.choices.items %} 17 | 21 | {% endfor %} 22 |
23 | {% else %} 24 | {% if field.allow_null or field.allow_blank %} 25 |
26 | 30 |
31 | {% endif %} 32 | {% for key, text in field.choices.items %} 33 |
34 | 38 |
39 | {% endfor %} 40 | {% endif %} 41 | {% if field.errors %} 42 | {% for error in field.errors %}{{ error }}{% endfor %} 43 | {% endif %} 44 | {% if field.help_text %} 45 | {{ field.help_text }} 46 | {% endif %} 47 |
48 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | def pytest_configure(): 2 | from django.conf import settings 3 | 4 | settings.configure( 5 | DEBUG_PROPAGATE_EXCEPTIONS=True, 6 | DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3', 7 | 'NAME': ':memory:'}}, 8 | SITE_ID=1, 9 | SECRET_KEY='not very secret in tests', 10 | USE_I18N=True, 11 | USE_L10N=True, 12 | STATIC_URL='/static/', 13 | ROOT_URLCONF='tests.urls', 14 | TEMPLATE_LOADERS=( 15 | 'django.template.loaders.filesystem.Loader', 16 | 'django.template.loaders.app_directories.Loader', 17 | ), 18 | MIDDLEWARE_CLASSES=( 19 | 'django.middleware.common.CommonMiddleware', 20 | 'django.contrib.sessions.middleware.SessionMiddleware', 21 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 22 | 'django.contrib.messages.middleware.MessageMiddleware', 23 | ), 24 | INSTALLED_APPS=( 25 | 'django.contrib.auth', 26 | 'django.contrib.contenttypes', 27 | 'django.contrib.sessions', 28 | 'django.contrib.sites', 29 | 'django.contrib.staticfiles', 30 | 31 | 'rest_framework', 32 | 'rest_framework.authtoken', 33 | 'tests', 34 | ), 35 | PASSWORD_HASHERS=( 36 | 'django.contrib.auth.hashers.MD5PasswordHasher', 37 | ), 38 | ) 39 | 40 | # guardian is optional 41 | try: 42 | import guardian # NOQA 43 | except ImportError: 44 | pass 45 | else: 46 | settings.ANONYMOUS_USER_ID = -1 47 | settings.AUTHENTICATION_BACKENDS = ( 48 | 'django.contrib.auth.backends.ModelBackend', 49 | 'guardian.backends.ObjectPermissionBackend', 50 | ) 51 | settings.INSTALLED_APPS += ( 52 | 'guardian', 53 | ) 54 | 55 | try: 56 | import django 57 | django.setup() 58 | except AttributeError: 59 | pass 60 | -------------------------------------------------------------------------------- /rest_framework/utils/formatting.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utility functions to return a formatted name and description for a given view. 3 | """ 4 | from __future__ import unicode_literals 5 | from django.utils.html import escape 6 | from django.utils.safestring import mark_safe 7 | from rest_framework.compat import apply_markdown, force_text 8 | import re 9 | 10 | 11 | def remove_trailing_string(content, trailing): 12 | """ 13 | Strip trailing component `trailing` from `content` if it exists. 14 | Used when generating names from view classes. 15 | """ 16 | if content.endswith(trailing) and content != trailing: 17 | return content[:-len(trailing)] 18 | return content 19 | 20 | 21 | def dedent(content): 22 | """ 23 | Remove leading indent from a block of text. 24 | Used when generating descriptions from docstrings. 25 | 26 | Note that python's `textwrap.dedent` doesn't quite cut it, 27 | as it fails to dedent multiline docstrings that include 28 | unindented text on the initial line. 29 | """ 30 | content = force_text(content) 31 | whitespace_counts = [len(line) - len(line.lstrip(' ')) 32 | for line in content.splitlines()[1:] if line.lstrip()] 33 | 34 | # unindent the content if needed 35 | if whitespace_counts: 36 | whitespace_pattern = '^' + (' ' * min(whitespace_counts)) 37 | content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', content) 38 | 39 | return content.strip() 40 | 41 | 42 | def camelcase_to_spaces(content): 43 | """ 44 | Translate 'CamelCaseNames' to 'Camel Case Names'. 45 | Used when generating names from view classes. 46 | """ 47 | camelcase_boundry = '(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))' 48 | content = re.sub(camelcase_boundry, ' \\1', content).strip() 49 | return ' '.join(content.split('_')).title() 50 | 51 | 52 | def markup_description(description): 53 | """ 54 | Apply HTML markup to the given description. 55 | """ 56 | if apply_markdown: 57 | description = apply_markdown(description) 58 | else: 59 | description = escape(description).replace('\n', '
') 60 | description = '

' + description + '

' 61 | return mark_safe(description) 62 | -------------------------------------------------------------------------------- /rest_framework/utils/breadcrumbs.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from django.core.urlresolvers import resolve, get_script_prefix 3 | 4 | 5 | def get_breadcrumbs(url): 6 | """ 7 | Given a url returns a list of breadcrumbs, which are each a 8 | tuple of (name, url). 9 | """ 10 | 11 | from rest_framework.settings import api_settings 12 | from rest_framework.views import APIView 13 | 14 | view_name_func = api_settings.VIEW_NAME_FUNCTION 15 | 16 | def breadcrumbs_recursive(url, breadcrumbs_list, prefix, seen): 17 | """ 18 | Add tuples of (name, url) to the breadcrumbs list, 19 | progressively chomping off parts of the url. 20 | """ 21 | 22 | try: 23 | (view, unused_args, unused_kwargs) = resolve(url) 24 | except Exception: 25 | pass 26 | else: 27 | # Check if this is a REST framework view, 28 | # and if so add it to the breadcrumbs 29 | cls = getattr(view, 'cls', None) 30 | if cls is not None and issubclass(cls, APIView): 31 | # Don't list the same view twice in a row. 32 | # Probably an optional trailing slash. 33 | if not seen or seen[-1] != view: 34 | suffix = getattr(view, 'suffix', None) 35 | name = view_name_func(cls, suffix) 36 | breadcrumbs_list.insert(0, (name, prefix + url)) 37 | seen.append(view) 38 | 39 | if url == '': 40 | # All done 41 | return breadcrumbs_list 42 | 43 | elif url.endswith('/'): 44 | # Drop trailing slash off the end and continue to try to 45 | # resolve more breadcrumbs 46 | url = url.rstrip('/') 47 | return breadcrumbs_recursive(url, breadcrumbs_list, prefix, seen) 48 | 49 | # Drop trailing non-slash off the end and continue to try to 50 | # resolve more breadcrumbs 51 | url = url[:url.rfind('/') + 1] 52 | return breadcrumbs_recursive(url, breadcrumbs_list, prefix, seen) 53 | 54 | prefix = get_script_prefix().rstrip('/') 55 | url = url[len(prefix):] 56 | return breadcrumbs_recursive(url, [], prefix, []) 57 | -------------------------------------------------------------------------------- /rest_framework/status.py: -------------------------------------------------------------------------------- 1 | """ 2 | Descriptive HTTP status codes, for code readability. 3 | 4 | See RFC 2616 - http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html 5 | And RFC 6585 - http://tools.ietf.org/html/rfc6585 6 | """ 7 | from __future__ import unicode_literals 8 | 9 | 10 | def is_informational(code): 11 | return code >= 100 and code <= 199 12 | 13 | 14 | def is_success(code): 15 | return code >= 200 and code <= 299 16 | 17 | 18 | def is_redirect(code): 19 | return code >= 300 and code <= 399 20 | 21 | 22 | def is_client_error(code): 23 | return code >= 400 and code <= 499 24 | 25 | 26 | def is_server_error(code): 27 | return code >= 500 and code <= 599 28 | 29 | 30 | HTTP_100_CONTINUE = 100 31 | HTTP_101_SWITCHING_PROTOCOLS = 101 32 | HTTP_200_OK = 200 33 | HTTP_201_CREATED = 201 34 | HTTP_202_ACCEPTED = 202 35 | HTTP_203_NON_AUTHORITATIVE_INFORMATION = 203 36 | HTTP_204_NO_CONTENT = 204 37 | HTTP_205_RESET_CONTENT = 205 38 | HTTP_206_PARTIAL_CONTENT = 206 39 | HTTP_300_MULTIPLE_CHOICES = 300 40 | HTTP_301_MOVED_PERMANENTLY = 301 41 | HTTP_302_FOUND = 302 42 | HTTP_303_SEE_OTHER = 303 43 | HTTP_304_NOT_MODIFIED = 304 44 | HTTP_305_USE_PROXY = 305 45 | HTTP_306_RESERVED = 306 46 | HTTP_307_TEMPORARY_REDIRECT = 307 47 | HTTP_400_BAD_REQUEST = 400 48 | HTTP_401_UNAUTHORIZED = 401 49 | HTTP_402_PAYMENT_REQUIRED = 402 50 | HTTP_403_FORBIDDEN = 403 51 | HTTP_404_NOT_FOUND = 404 52 | HTTP_405_METHOD_NOT_ALLOWED = 405 53 | HTTP_406_NOT_ACCEPTABLE = 406 54 | HTTP_407_PROXY_AUTHENTICATION_REQUIRED = 407 55 | HTTP_408_REQUEST_TIMEOUT = 408 56 | HTTP_409_CONFLICT = 409 57 | HTTP_410_GONE = 410 58 | HTTP_411_LENGTH_REQUIRED = 411 59 | HTTP_412_PRECONDITION_FAILED = 412 60 | HTTP_413_REQUEST_ENTITY_TOO_LARGE = 413 61 | HTTP_414_REQUEST_URI_TOO_LONG = 414 62 | HTTP_415_UNSUPPORTED_MEDIA_TYPE = 415 63 | HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE = 416 64 | HTTP_417_EXPECTATION_FAILED = 417 65 | HTTP_428_PRECONDITION_REQUIRED = 428 66 | HTTP_429_TOO_MANY_REQUESTS = 429 67 | HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE = 431 68 | HTTP_500_INTERNAL_SERVER_ERROR = 500 69 | HTTP_501_NOT_IMPLEMENTED = 501 70 | HTTP_502_BAD_GATEWAY = 502 71 | HTTP_503_SERVICE_UNAVAILABLE = 503 72 | HTTP_504_GATEWAY_TIMEOUT = 504 73 | HTTP_505_HTTP_VERSION_NOT_SUPPORTED = 505 74 | HTTP_511_NETWORK_AUTHENTICATION_REQUIRED = 511 75 | -------------------------------------------------------------------------------- /tests/models.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from django.db import models 3 | from django.utils.translation import ugettext_lazy as _ 4 | 5 | 6 | class RESTFrameworkModel(models.Model): 7 | """ 8 | Base for test models that sets app_label, so they play nicely. 9 | """ 10 | 11 | class Meta: 12 | app_label = 'tests' 13 | abstract = True 14 | 15 | 16 | class BasicModel(RESTFrameworkModel): 17 | text = models.CharField(max_length=100, verbose_name=_("Text comes here"), help_text=_("Text description.")) 18 | 19 | 20 | class BaseFilterableItem(RESTFrameworkModel): 21 | text = models.CharField(max_length=100) 22 | 23 | class Meta: 24 | abstract = True 25 | 26 | 27 | class FilterableItem(BaseFilterableItem): 28 | decimal = models.DecimalField(max_digits=4, decimal_places=2) 29 | date = models.DateField() 30 | 31 | 32 | # Models for relations tests 33 | # ManyToMany 34 | class ManyToManyTarget(RESTFrameworkModel): 35 | name = models.CharField(max_length=100) 36 | 37 | 38 | class ManyToManySource(RESTFrameworkModel): 39 | name = models.CharField(max_length=100) 40 | targets = models.ManyToManyField(ManyToManyTarget, related_name='sources') 41 | 42 | 43 | # ForeignKey 44 | class ForeignKeyTarget(RESTFrameworkModel): 45 | name = models.CharField(max_length=100) 46 | 47 | 48 | class ForeignKeySource(RESTFrameworkModel): 49 | name = models.CharField(max_length=100) 50 | target = models.ForeignKey(ForeignKeyTarget, related_name='sources', 51 | help_text='Target', verbose_name='Target') 52 | 53 | 54 | # Nullable ForeignKey 55 | class NullableForeignKeySource(RESTFrameworkModel): 56 | name = models.CharField(max_length=100) 57 | target = models.ForeignKey(ForeignKeyTarget, null=True, blank=True, 58 | related_name='nullable_sources', 59 | verbose_name='Optional target object') 60 | 61 | 62 | # OneToOne 63 | class OneToOneTarget(RESTFrameworkModel): 64 | name = models.CharField(max_length=100) 65 | 66 | 67 | class NullableOneToOneSource(RESTFrameworkModel): 68 | name = models.CharField(max_length=100) 69 | target = models.OneToOneField(OneToOneTarget, null=True, blank=True, 70 | related_name='nullable_source') 71 | -------------------------------------------------------------------------------- /tests/utils.py: -------------------------------------------------------------------------------- 1 | from django.core.exceptions import ObjectDoesNotExist 2 | from django.core.urlresolvers import NoReverseMatch 3 | 4 | 5 | class UsingURLPatterns(object): 6 | """ 7 | Isolates URL patterns used during testing on the test class itself. 8 | For example: 9 | 10 | class MyTestCase(UsingURLPatterns, TestCase): 11 | urlpatterns = [ 12 | ... 13 | ] 14 | 15 | def test_something(self): 16 | ... 17 | """ 18 | urls = __name__ 19 | 20 | def setUp(self): 21 | global urlpatterns 22 | urlpatterns = self.urlpatterns 23 | 24 | def tearDown(self): 25 | global urlpatterns 26 | urlpatterns = [] 27 | 28 | 29 | class MockObject(object): 30 | def __init__(self, **kwargs): 31 | self._kwargs = kwargs 32 | for key, val in kwargs.items(): 33 | setattr(self, key, val) 34 | 35 | def __str__(self): 36 | kwargs_str = ', '.join([ 37 | '%s=%s' % (key, value) 38 | for key, value in sorted(self._kwargs.items()) 39 | ]) 40 | return '' % kwargs_str 41 | 42 | 43 | class MockQueryset(object): 44 | def __init__(self, iterable): 45 | self.items = iterable 46 | 47 | def get(self, **lookup): 48 | for item in self.items: 49 | if all([ 50 | getattr(item, key, None) == value 51 | for key, value in lookup.items() 52 | ]): 53 | return item 54 | raise ObjectDoesNotExist() 55 | 56 | 57 | class BadType(object): 58 | """ 59 | When used as a lookup with a `MockQueryset`, these objects 60 | will raise a `TypeError`, as occurs in Django when making 61 | queryset lookups with an incorrect type for the lookup value. 62 | """ 63 | def __eq__(self): 64 | raise TypeError() 65 | 66 | 67 | def mock_reverse(view_name, args=None, kwargs=None, request=None, format=None): 68 | args = args or [] 69 | kwargs = kwargs or {} 70 | value = (args + list(kwargs.values()) + ['-'])[0] 71 | prefix = 'http://example.org' if request else '' 72 | suffix = ('.' + format) if (format is not None) else '' 73 | return '%s/%s/%s%s/' % (prefix, view_name, value, suffix) 74 | 75 | 76 | def fail_reverse(view_name, args=None, kwargs=None, request=None, format=None): 77 | raise NoReverseMatch() 78 | -------------------------------------------------------------------------------- /tests/test_multitable_inheritance.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from django.db import models 3 | from django.test import TestCase 4 | from rest_framework import serializers 5 | from tests.models import RESTFrameworkModel 6 | 7 | 8 | # Models 9 | class ParentModel(RESTFrameworkModel): 10 | name1 = models.CharField(max_length=100) 11 | 12 | 13 | class ChildModel(ParentModel): 14 | name2 = models.CharField(max_length=100) 15 | 16 | 17 | class AssociatedModel(RESTFrameworkModel): 18 | ref = models.OneToOneField(ParentModel, primary_key=True) 19 | name = models.CharField(max_length=100) 20 | 21 | 22 | # Serializers 23 | class DerivedModelSerializer(serializers.ModelSerializer): 24 | class Meta: 25 | model = ChildModel 26 | 27 | 28 | class AssociatedModelSerializer(serializers.ModelSerializer): 29 | class Meta: 30 | model = AssociatedModel 31 | 32 | 33 | # Tests 34 | class InheritedModelSerializationTests(TestCase): 35 | 36 | def test_multitable_inherited_model_fields_as_expected(self): 37 | """ 38 | Assert that the parent pointer field is not included in the fields 39 | serialized fields 40 | """ 41 | child = ChildModel(name1='parent name', name2='child name') 42 | serializer = DerivedModelSerializer(child) 43 | self.assertEqual(set(serializer.data.keys()), 44 | set(['name1', 'name2', 'id'])) 45 | 46 | def test_onetoone_primary_key_model_fields_as_expected(self): 47 | """ 48 | Assert that a model with a onetoone field that is the primary key is 49 | not treated like a derived model 50 | """ 51 | parent = ParentModel.objects.create(name1='parent name') 52 | associate = AssociatedModel.objects.create(name='hello', ref=parent) 53 | serializer = AssociatedModelSerializer(associate) 54 | self.assertEqual(set(serializer.data.keys()), 55 | set(['name', 'ref'])) 56 | 57 | def test_data_is_valid_without_parent_ptr(self): 58 | """ 59 | Assert that the pointer to the parent table is not a required field 60 | for input data 61 | """ 62 | data = { 63 | 'name1': 'parent name', 64 | 'name2': 'child name', 65 | } 66 | serializer = DerivedModelSerializer(data=data) 67 | self.assertEqual(serializer.is_valid(), True) 68 | -------------------------------------------------------------------------------- /rest_framework/utils/html.py: -------------------------------------------------------------------------------- 1 | """ 2 | Helpers for dealing with HTML input. 3 | """ 4 | import re 5 | from django.utils.datastructures import MultiValueDict 6 | 7 | 8 | def is_html_input(dictionary): 9 | # MultiDict type datastructures are used to represent HTML form input, 10 | # which may have more than one value for each key. 11 | return hasattr(dictionary, 'getlist') 12 | 13 | 14 | def parse_html_list(dictionary, prefix=''): 15 | """ 16 | Used to suport list values in HTML forms. 17 | Supports lists of primitives and/or dictionaries. 18 | 19 | * List of primitives. 20 | 21 | { 22 | '[0]': 'abc', 23 | '[1]': 'def', 24 | '[2]': 'hij' 25 | } 26 | --> 27 | [ 28 | 'abc', 29 | 'def', 30 | 'hij' 31 | ] 32 | 33 | * List of dictionaries. 34 | 35 | { 36 | '[0]foo': 'abc', 37 | '[0]bar': 'def', 38 | '[1]foo': 'hij', 39 | '[1]bar': 'klm', 40 | } 41 | --> 42 | [ 43 | {'foo': 'abc', 'bar': 'def'}, 44 | {'foo': 'hij', 'bar': 'klm'} 45 | ] 46 | """ 47 | ret = {} 48 | regex = re.compile(r'^%s\[([0-9]+)\](.*)$' % re.escape(prefix)) 49 | for field, value in dictionary.items(): 50 | match = regex.match(field) 51 | if not match: 52 | continue 53 | index, key = match.groups() 54 | index = int(index) 55 | if not key: 56 | ret[index] = value 57 | elif isinstance(ret.get(index), dict): 58 | ret[index][key] = value 59 | else: 60 | ret[index] = MultiValueDict({key: [value]}) 61 | return [ret[item] for item in sorted(ret.keys())] 62 | 63 | 64 | def parse_html_dict(dictionary, prefix): 65 | """ 66 | Used to support dictionary values in HTML forms. 67 | 68 | { 69 | 'profile.username': 'example', 70 | 'profile.email': 'example@example.com', 71 | } 72 | --> 73 | { 74 | 'profile': { 75 | 'username': 'example', 76 | 'email': 'example@example.com' 77 | } 78 | } 79 | """ 80 | ret = {} 81 | regex = re.compile(r'^%s\.(.+)$' % re.escape(prefix)) 82 | for field, value in dictionary.items(): 83 | match = regex.match(field) 84 | if not match: 85 | continue 86 | key = match.groups()[0] 87 | ret[key] = value 88 | return ret 89 | -------------------------------------------------------------------------------- /rest_framework/utils/encoders.py: -------------------------------------------------------------------------------- 1 | """ 2 | Helper classes for parsers. 3 | """ 4 | from __future__ import unicode_literals 5 | from django.db.models.query import QuerySet 6 | from django.utils import six, timezone 7 | from django.utils.encoding import force_text 8 | from django.utils.functional import Promise 9 | from rest_framework.compat import total_seconds 10 | import datetime 11 | import decimal 12 | import json 13 | import uuid 14 | 15 | 16 | class JSONEncoder(json.JSONEncoder): 17 | """ 18 | JSONEncoder subclass that knows how to encode date/time/timedelta, 19 | decimal types, generators and other basic python objects. 20 | """ 21 | def default(self, obj): 22 | # For Date Time string spec, see ECMA 262 23 | # http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15 24 | if isinstance(obj, Promise): 25 | return force_text(obj) 26 | elif isinstance(obj, datetime.datetime): 27 | representation = obj.isoformat() 28 | if obj.microsecond: 29 | representation = representation[:23] + representation[26:] 30 | if representation.endswith('+00:00'): 31 | representation = representation[:-6] + 'Z' 32 | return representation 33 | elif isinstance(obj, datetime.date): 34 | return obj.isoformat() 35 | elif isinstance(obj, datetime.time): 36 | if timezone and timezone.is_aware(obj): 37 | raise ValueError("JSON can't represent timezone-aware times.") 38 | representation = obj.isoformat() 39 | if obj.microsecond: 40 | representation = representation[:12] 41 | return representation 42 | elif isinstance(obj, datetime.timedelta): 43 | return six.text_type(total_seconds(obj)) 44 | elif isinstance(obj, decimal.Decimal): 45 | # Serializers will coerce decimals to strings by default. 46 | return float(obj) 47 | elif isinstance(obj, uuid.UUID): 48 | return six.text_type(obj) 49 | elif isinstance(obj, QuerySet): 50 | return tuple(obj) 51 | elif hasattr(obj, 'tolist'): 52 | # Numpy arrays and array scalars. 53 | return obj.tolist() 54 | elif hasattr(obj, '__getitem__'): 55 | try: 56 | return dict(obj) 57 | except: 58 | pass 59 | elif hasattr(obj, '__iter__'): 60 | return tuple(item for item in obj) 61 | return super(JSONEncoder, self).default(obj) 62 | -------------------------------------------------------------------------------- /tests/browsable_api/test_browsable_api.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from django.contrib.auth.models import User 3 | from django.test import TestCase 4 | 5 | from rest_framework.test import APIClient 6 | 7 | 8 | class DropdownWithAuthTests(TestCase): 9 | """Tests correct dropdown behaviour with Auth views enabled.""" 10 | 11 | urls = 'tests.browsable_api.auth_urls' 12 | 13 | def setUp(self): 14 | self.client = APIClient(enforce_csrf_checks=True) 15 | self.username = 'john' 16 | self.email = 'lennon@thebeatles.com' 17 | self.password = 'password' 18 | self.user = User.objects.create_user(self.username, self.email, self.password) 19 | 20 | def tearDown(self): 21 | self.client.logout() 22 | 23 | def test_name_shown_when_logged_in(self): 24 | self.client.login(username=self.username, password=self.password) 25 | response = self.client.get('/') 26 | self.assertContains(response, 'john') 27 | 28 | def test_logout_shown_when_logged_in(self): 29 | self.client.login(username=self.username, password=self.password) 30 | response = self.client.get('/') 31 | self.assertContains(response, '>Log out<') 32 | 33 | def test_login_shown_when_logged_out(self): 34 | response = self.client.get('/') 35 | self.assertContains(response, '>Log in<') 36 | 37 | 38 | class NoDropdownWithoutAuthTests(TestCase): 39 | """Tests correct dropdown behaviour with Auth views NOT enabled.""" 40 | 41 | urls = 'tests.browsable_api.no_auth_urls' 42 | 43 | def setUp(self): 44 | self.client = APIClient(enforce_csrf_checks=True) 45 | self.username = 'john' 46 | self.email = 'lennon@thebeatles.com' 47 | self.password = 'password' 48 | self.user = User.objects.create_user(self.username, self.email, self.password) 49 | 50 | def tearDown(self): 51 | self.client.logout() 52 | 53 | def test_name_shown_when_logged_in(self): 54 | self.client.login(username=self.username, password=self.password) 55 | response = self.client.get('/') 56 | self.assertContains(response, 'john') 57 | 58 | def test_dropdown_not_shown_when_logged_in(self): 59 | self.client.login(username=self.username, password=self.password) 60 | response = self.client.get('/') 61 | self.assertNotContains(response, '