Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
D
django-rest-framework
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
django-rest-framework
Commits
279fa0d3
Commit
279fa0d3
authored
Jan 31, 2012
by
Sébastien Piquemal
Browse files
Options
Browse Files
Download
Plain Diff
merge
parents
152c385f
b2fcfffb
Hide whitespace changes
Inline
Side-by-side
Showing
28 changed files
with
1439 additions
and
235 deletions
+1439
-235
AUTHORS
+2
-0
CHANGELOG.rst
+40
-0
README.rst
+1
-1
djangorestframework/__init__.py
+1
-1
djangorestframework/authentication.py
+2
-15
djangorestframework/mixins.py
+0
-5
djangorestframework/permissions.py
+1
-1
djangorestframework/renderers.py
+17
-12
djangorestframework/runtests/settings.py
+6
-0
djangorestframework/static/css/djangorestframework.css
+1152
-0
djangorestframework/status.py
+0
-1
djangorestframework/templates/renderer.html
+10
-21
djangorestframework/templates/renderer.txt
+2
-2
djangorestframework/templatetags/add_query_param.py
+1
-1
djangorestframework/tests/authentication.py
+13
-5
djangorestframework/tests/description.py
+27
-27
djangorestframework/tests/mixins.py
+2
-3
djangorestframework/tests/renderers.py
+10
-1
djangorestframework/tests/validators.py
+22
-27
djangorestframework/utils/breadcrumbs.py
+1
-3
djangorestframework/utils/description.py
+0
-88
djangorestframework/views.py
+97
-5
docs/index.rst
+2
-0
examples/permissionsexample/views.py
+17
-6
examples/requirements-epio.txt
+1
-1
examples/settings.py
+4
-9
examples/urls.py
+2
-0
tox.ini
+6
-0
No files found.
AUTHORS
View file @
279fa0d3
...
@@ -27,6 +27,8 @@ Natim <natim>
...
@@ -27,6 +27,8 @@ Natim <natim>
Sebastian Żurek <sebzur>
Sebastian Żurek <sebzur>
Benoit C <dzen>
Benoit C <dzen>
Chris Pickett <bunchesofdonald>
Chris Pickett <bunchesofdonald>
Ben Timby <btimby>
Michele Lazzeri <michelelazzeri-nextage>
THANKS TO:
THANKS TO:
...
...
RELEASES
→
CHANGELOG.rst
View file @
279fa0d3
Release Notes
=============
development
-----------
* Saner template variable autoescaping.
* Use `staticfiles` for css files.
- Easier to override. Won't conflict with customised admin styles (eg grappelli)
* Drop implied 'pk' filter if last arg in urlconf is unnamed.
- Too magical. Explict is better than implicit.
* Bugfixes:
- Bug with PerUserThrottling when user contains unicode chars.
0.3.2
-----
* Bugfixes:
* Fix 403 for POST and PUT from the UI with UserLoggedInAuthentication (#115)
* serialize_model method in serializer.py may cause wrong value (#73)
* Fix Error when clicking OPTIONS button (#146)
* And many other fixes
* Remove short status codes
- Zen of Python: "There should be one-- and preferably only one --obvious way to do it."
* get_name, get_description become methods on the view - makes them overridable.
* Improved model mixin API - Hooks for build_query, get_instance_data, get_model, get_queryset, get_ordering
0.3.1
-----
* [not documented]
0.3.0
0.3.0
-----
* JSONP Support
* JSONP Support
* Bugfixes, including support for latest markdown release
* Bugfixes, including support for latest markdown release
0.2.4
0.2.4
-----
* Fix broken IsAdminUser permission.
* Fix broken IsAdminUser permission.
* OPTIONS support.
* OPTIONS support.
...
@@ -11,20 +45,24 @@
...
@@ -11,20 +45,24 @@
* Drop mentions of Blog, BitBucket.
* Drop mentions of Blog, BitBucket.
0.2.3
0.2.3
-----
* Fix some throttling bugs.
* Fix some throttling bugs.
* ``X-Throttle`` header on throttling.
* ``X-Throttle`` header on throttling.
* Support for nesting resources on related models.
* Support for nesting resources on related models.
0.2.2
0.2.2
-----
* Throttling support complete.
* Throttling support complete.
0.2.1
0.2.1
-----
* Couple of simple bugfixes over 0.2.0
* Couple of simple bugfixes over 0.2.0
0.2.0
0.2.0
-----
* Big refactoring changes since 0.1.0, ask on the discussion group if anything isn't clear.
* Big refactoring changes since 0.1.0, ask on the discussion group if anything isn't clear.
The public API has been massively cleaned up. Expect it to be fairly stable from here on in.
The public API has been massively cleaned up. Expect it to be fairly stable from here on in.
...
@@ -49,9 +87,11 @@
...
@@ -49,9 +87,11 @@
You can reuse these mixin classes individually without using the ``View`` class.
You can reuse these mixin classes individually without using the ``View`` class.
0.1.1
0.1.1
-----
* Final build before pulling in all the refactoring changes for 0.2, in case anyone needs to hang on to 0.1.
* Final build before pulling in all the refactoring changes for 0.2, in case anyone needs to hang on to 0.1.
0.1.0
0.1.0
-----
* Initial release.
* Initial release.
README.rst
View file @
279fa0d3
...
@@ -16,7 +16,7 @@ Full documentation for the project is available at http://django-rest-framework.
...
@@ -16,7 +16,7 @@ Full documentation for the project is available at http://django-rest-framework.
Issue tracking is on `GitHub <https://github.com/tomchristie/django-rest-framework/issues>`_.
Issue tracking is on `GitHub <https://github.com/tomchristie/django-rest-framework/issues>`_.
General questions should be taken to the `discussion group <http://groups.google.com/group/django-rest-framework>`_.
General questions should be taken to the `discussion group <http://groups.google.com/group/django-rest-framework>`_.
We also have a `Jenkins service <http://jenkins.tibold.nl/job/djangorestframework/>`_ which runs our test suite.
We also have a `Jenkins service <http://jenkins.tibold.nl/job/djangorestframework
1
/>`_ which runs our test suite.
Requirements:
Requirements:
...
...
djangorestframework/__init__.py
View file @
279fa0d3
__version__
=
'0.3.
2
-dev'
__version__
=
'0.3.
3
-dev'
VERSION
=
__version__
# synonym
VERSION
=
__version__
# synonym
djangorestframework/authentication.py
View file @
279fa0d3
...
@@ -87,25 +87,12 @@ class UserLoggedInAuthentication(BaseAuthentication):
...
@@ -87,25 +87,12 @@ class UserLoggedInAuthentication(BaseAuthentication):
Returns a :obj:`User` if the request session currently has a logged in user.
Returns a :obj:`User` if the request session currently has a logged in user.
Otherwise returns :const:`None`.
Otherwise returns :const:`None`.
"""
"""
# TODO: Might be cleaner to switch this back to using request.POST,
request
.
DATA
# Make sure our generic parsing runs first
# and let FormParser/MultiPartParser deal with the consequences.
if
getattr
(
request
,
'user'
,
None
)
and
request
.
user
.
is_active
:
if
getattr
(
request
,
'user'
,
None
)
and
request
.
user
.
is_active
:
# Enforce CSRF validation for session based authentication.
# Enforce CSRF validation for session based authentication.
# Temporarily replace request.POST with .DATA, to use our generic parsing.
# If DATA is not dict-like, use an empty dict.
if
request
.
method
.
upper
()
==
'POST'
:
if
hasattr
(
request
.
DATA
,
'get'
):
request
.
_post
=
request
.
DATA
else
:
request
.
_post
=
{}
resp
=
CsrfViewMiddleware
()
.
process_view
(
request
,
None
,
(),
{})
resp
=
CsrfViewMiddleware
()
.
process_view
(
request
,
None
,
(),
{})
# Replace request.POST
if
request
.
method
.
upper
()
==
'POST'
:
del
(
request
.
_post
)
if
resp
is
None
:
# csrf passed
if
resp
is
None
:
# csrf passed
return
request
.
user
return
request
.
user
return
None
return
None
...
...
djangorestframework/mixins.py
View file @
279fa0d3
...
@@ -384,11 +384,6 @@ class ModelMixin(object):
...
@@ -384,11 +384,6 @@ class ModelMixin(object):
if
BaseRenderer
.
_FORMAT_QUERY_PARAM
in
tmp
:
if
BaseRenderer
.
_FORMAT_QUERY_PARAM
in
tmp
:
del
tmp
[
BaseRenderer
.
_FORMAT_QUERY_PARAM
]
del
tmp
[
BaseRenderer
.
_FORMAT_QUERY_PARAM
]
if
args
:
# If we have any no kwargs then assume the last arg represents the
# primrary key. Otherwise assume the kwargs uniquely identify the
# model.
tmp
.
update
({
'pk'
:
args
[
-
1
]})
return
Q
(
**
tmp
)
return
Q
(
**
tmp
)
def
get_instance_data
(
self
,
model
,
content
,
**
kwargs
):
def
get_instance_data
(
self
,
model
,
content
,
**
kwargs
):
...
...
djangorestframework/permissions.py
View file @
279fa0d3
...
@@ -188,7 +188,7 @@ class PerUserThrottling(BaseThrottle):
...
@@ -188,7 +188,7 @@ class PerUserThrottling(BaseThrottle):
def
get_cache_key
(
self
):
def
get_cache_key
(
self
):
if
self
.
auth
.
is_authenticated
():
if
self
.
auth
.
is_authenticated
():
ident
=
s
tr
(
self
.
auth
)
ident
=
s
elf
.
auth
.
id
else
:
else
:
ident
=
self
.
view
.
request
.
META
.
get
(
'REMOTE_ADDR'
,
None
)
ident
=
self
.
view
.
request
.
META
.
get
(
'REMOTE_ADDR'
,
None
)
return
'throttle_user_
%
s'
%
ident
return
'throttle_user_
%
s'
%
ident
...
...
djangorestframework/renderers.py
View file @
279fa0d3
...
@@ -12,10 +12,9 @@ from django.template import RequestContext, loader
...
@@ -12,10 +12,9 @@ from django.template import RequestContext, loader
from
django.utils
import
simplejson
as
json
from
django.utils
import
simplejson
as
json
from
djangorestframework.compat
import
apply_markdown
,
yaml
from
djangorestframework.compat
import
yaml
from
djangorestframework.utils
import
dict2xml
,
url_resolves
from
djangorestframework.utils
import
dict2xml
,
url_resolves
from
djangorestframework.utils.breadcrumbs
import
get_breadcrumbs
from
djangorestframework.utils.breadcrumbs
import
get_breadcrumbs
from
djangorestframework.utils.description
import
get_name
,
get_description
from
djangorestframework.utils.mediatypes
import
get_media_type_params
,
add_media_type_param
,
media_type_matches
from
djangorestframework.utils.mediatypes
import
get_media_type_params
,
add_media_type_param
,
media_type_matches
from
djangorestframework
import
VERSION
from
djangorestframework
import
VERSION
...
@@ -296,6 +295,20 @@ class DocumentingTemplateRenderer(BaseRenderer):
...
@@ -296,6 +295,20 @@ class DocumentingTemplateRenderer(BaseRenderer):
# Okey doke, let's do it
# Okey doke, let's do it
return
GenericContentForm
(
view
.
request
)
return
GenericContentForm
(
view
.
request
)
def
get_name
(
self
):
try
:
return
self
.
view
.
get_name
()
except
AttributeError
:
return
self
.
view
.
__doc__
def
get_description
(
self
,
html
=
None
):
if
html
is
None
:
html
=
bool
(
'html'
in
self
.
format
)
try
:
return
self
.
view
.
get_description
(
html
)
except
AttributeError
:
return
self
.
view
.
__doc__
def
render
(
self
,
obj
=
None
,
media_type
=
None
):
def
render
(
self
,
obj
=
None
,
media_type
=
None
):
"""
"""
Renders *obj* using the :attr:`template` set on the class.
Renders *obj* using the :attr:`template` set on the class.
...
@@ -316,15 +329,8 @@ class DocumentingTemplateRenderer(BaseRenderer):
...
@@ -316,15 +329,8 @@ class DocumentingTemplateRenderer(BaseRenderer):
login_url
=
None
login_url
=
None
logout_url
=
None
logout_url
=
None
name
=
get_name
(
self
.
view
)
name
=
self
.
get_name
()
description
=
get_description
(
self
.
view
)
description
=
self
.
get_description
()
markeddown
=
None
if
apply_markdown
:
try
:
markeddown
=
apply_markdown
(
description
)
except
AttributeError
:
markeddown
=
None
breadcrumb_list
=
get_breadcrumbs
(
self
.
view
.
request
.
path
)
breadcrumb_list
=
get_breadcrumbs
(
self
.
view
.
request
.
path
)
...
@@ -337,7 +343,6 @@ class DocumentingTemplateRenderer(BaseRenderer):
...
@@ -337,7 +343,6 @@ class DocumentingTemplateRenderer(BaseRenderer):
'description'
:
description
,
'description'
:
description
,
'name'
:
name
,
'name'
:
name
,
'version'
:
VERSION
,
'version'
:
VERSION
,
'markeddown'
:
markeddown
,
'breadcrumblist'
:
breadcrumb_list
,
'breadcrumblist'
:
breadcrumb_list
,
'available_formats'
:
self
.
view
.
_rendered_formats
,
'available_formats'
:
self
.
view
.
_rendered_formats
,
'put_form'
:
put_form_instance
,
'put_form'
:
put_form_instance
,
...
...
djangorestframework/runtests/settings.py
View file @
279fa0d3
...
@@ -97,6 +97,12 @@ INSTALLED_APPS = (
...
@@ -97,6 +97,12 @@ INSTALLED_APPS = (
'djangorestframework'
,
'djangorestframework'
,
)
)
import
django
if
django
.
VERSION
<
(
1
,
3
):
INSTALLED_APPS
+=
(
'staticfiles'
,)
# OAuth support is optional, so we only test oauth if it's installed.
# OAuth support is optional, so we only test oauth if it's installed.
try
:
try
:
import
oauth_provider
import
oauth_provider
...
...
djangorestframework/static/css/djangorestframework.css
0 → 100644
View file @
279fa0d3
/********************** admin 'base.css' ************************/
body
{
margin
:
0
;
padding
:
0
;
font-size
:
12px
;
font-family
:
"Lucida Grande"
,
"DejaVu Sans"
,
"Bitstream Vera Sans"
,
Verdana
,
Arial
,
sans-serif
;
color
:
#333
;
background
:
#fff
;
}
/* LINKS */
a
:link
,
a
:visited
{
color
:
#5b80b2
;
text-decoration
:
none
;
}
a
:hover
{
color
:
#036
;
}
a
img
{
border
:
none
;
}
a
.section
:link
,
a
.section
:visited
{
color
:
white
;
text-decoration
:
none
;
}
/* GLOBAL DEFAULTS */
p
,
ol
,
ul
,
dl
{
margin
:
.2em
0
.8em
0
;
}
p
{
padding
:
0
;
line-height
:
140%
;
}
h1
,
h2
,
h3
,
h4
,
h5
{
font-weight
:
bold
;
}
h1
{
font-size
:
18px
;
color
:
#666
;
padding
:
0
6px
0
0
;
margin
:
0
0
.2em
0
;
}
h2
{
font-size
:
16px
;
margin
:
1em
0
.5em
0
;
}
h2
.subhead
{
font-weight
:
normal
;
margin-top
:
0
;
}
h3
{
font-size
:
14px
;
margin
:
.8em
0
.3em
0
;
color
:
#666
;
font-weight
:
bold
;
}
h4
{
font-size
:
12px
;
margin
:
1em
0
.8em
0
;
padding-bottom
:
3px
;
}
h5
{
font-size
:
10px
;
margin
:
1.5em
0
.5em
0
;
color
:
#666
;
text-transform
:
uppercase
;
letter-spacing
:
1px
;
}
ul
li
{
list-style-type
:
square
;
padding
:
1px
0
;
}
ul
.plainlist
{
margin-left
:
0
!important
;
}
ul
.plainlist
li
{
list-style-type
:
none
;
}
li
ul
{
margin-bottom
:
0
;
}
li
,
dt
,
dd
{
font-size
:
11px
;
line-height
:
14px
;
}
dt
{
font-weight
:
bold
;
margin-top
:
4px
;
}
dd
{
margin-left
:
0
;
}
form
{
margin
:
0
;
padding
:
0
;
}
fieldset
{
margin
:
0
;
padding
:
0
;
}
blockquote
{
font-size
:
11px
;
color
:
#777
;
margin-left
:
2px
;
padding-left
:
10px
;
border-left
:
5px
solid
#ddd
;
}
code
,
pre
{
font-family
:
"Bitstream Vera Sans Mono"
,
Monaco
,
"Courier New"
,
Courier
,
monospace
;
background
:
inherit
;
color
:
#666
;
font-size
:
11px
;
}
pre
.literal-block
{
margin
:
10px
;
background
:
#eee
;
padding
:
6px
8px
;
}
code
strong
{
color
:
#930
;
}
hr
{
clear
:
both
;
color
:
#eee
;
background-color
:
#eee
;
height
:
1px
;
border
:
none
;
margin
:
0
;
padding
:
0
;
font-size
:
1px
;
line-height
:
1px
;
}
/* TEXT STYLES & MODIFIERS */
.small
{
font-size
:
11px
;
}
.tiny
{
font-size
:
10px
;
}
p
.tiny
{
margin-top
:
-2px
;
}
.mini
{
font-size
:
9px
;
}
p
.mini
{
margin-top
:
-3px
;
}
.help
,
p
.help
{
font-size
:
10px
!important
;
color
:
#999
;
}
p
img
,
h1
img
,
h2
img
,
h3
img
,
h4
img
,
td
img
{
vertical-align
:
middle
;
}
.quiet
,
a
.quiet
:link
,
a
.quiet
:visited
{
color
:
#999
!important
;
font-weight
:
normal
!important
;
}
.quiet
strong
{
font-weight
:
bold
!important
;
}
.float-right
{
float
:
right
;
}
.float-left
{
float
:
left
;
}
.clear
{
clear
:
both
;
}
.align-left
{
text-align
:
left
;
}
.align-right
{
text-align
:
right
;
}
.example
{
margin
:
10px
0
;
padding
:
5px
10px
;
background
:
#efefef
;
}
.nowrap
{
white-space
:
nowrap
;
}
/* TABLES */
table
{
border-collapse
:
collapse
;
border-color
:
#ccc
;
}
td
,
th
{
font-size
:
11px
;
line-height
:
13px
;
border-bottom
:
1px
solid
#eee
;
vertical-align
:
top
;
padding
:
5px
;
font-family
:
"Lucida Grande"
,
Verdana
,
Arial
,
sans-serif
;
}
th
{
text-align
:
left
;
font-size
:
12px
;
font-weight
:
bold
;
}
thead
th
,
tfoot
td
{
color
:
#666
;
padding
:
2px
5px
;
font-size
:
11px
;
background
:
#e1e1e1
url(../img/admin/nav-bg.gif)
top
left
repeat-x
;
border-left
:
1px
solid
#ddd
;
border-bottom
:
1px
solid
#ddd
;
}
tfoot
td
{
border-bottom
:
none
;
border-top
:
1px
solid
#ddd
;
}
thead
th
:first-child
,
tfoot
td
:first-child
{
border-left
:
none
!important
;
}
thead
th
.optional
{
font-weight
:
normal
!important
;
}
fieldset
table
{
border-right
:
1px
solid
#eee
;
}
tr
.row-label
td
{
font-size
:
9px
;
padding-top
:
2px
;
padding-bottom
:
0
;
border-bottom
:
none
;
color
:
#666
;
margin-top
:
-1px
;
}
tr
.alt
{
background
:
#f6f6f6
;
}
.row1
{
background
:
#EDF3FE
;
}
.row2
{
background
:
white
;
}
/* SORTABLE TABLES */
thead
th
a
:link
,
thead
th
a
:visited
{
color
:
#666
;
display
:
block
;
}
table
thead
th
.sorted
{
background-position
:
bottom
left
!important
;
}
table
thead
th
.sorted
a
{
padding-right
:
13px
;
}
table
thead
th
.ascending
a
{
background
:
url(../img/admin/arrow-up.gif)
right
.4em
no-repeat
;
}
table
thead
th
.descending
a
{
background
:
url(../img/admin/arrow-down.gif)
right
.4em
no-repeat
;
}
/* ORDERABLE TABLES */
table
.orderable
tbody
tr
td
:hover
{
cursor
:
move
;
}
table
.orderable
tbody
tr
td
:first-child
{
padding-left
:
14px
;
background-image
:
url(../img/admin/nav-bg-grabber.gif)
;
background-repeat
:
repeat-y
;
}
table
.orderable-initalized
.order-cell
,
body
>
tr
>
td
.order-cell
{
display
:
none
;
}
/* FORM DEFAULTS */
input
,
textarea
,
select
,
.form-row
p
{
margin
:
2px
0
;
padding
:
2px
3px
;
vertical-align
:
middle
;
font-family
:
"Lucida Grande"
,
Verdana
,
Arial
,
sans-serif
;
font-weight
:
normal
;
font-size
:
11px
;
}
textarea
{
vertical-align
:
top
!important
;
}
input
[
type
=
text
],
input
[
type
=
password
],
textarea
,
select
,
.vTextField
{
border
:
1px
solid
#ccc
;
}
/* FORM BUTTONS */
.button
,
input
[
type
=
submit
],
input
[
type
=
button
],
.submit-row
input
{
background
:
white
url(../img/admin/nav-bg.gif)
bottom
repeat-x
;
padding
:
3px
5px
;
color
:
black
;
border
:
1px
solid
#bbb
;
border-color
:
#ddd
#aaa
#aaa
#ddd
;
}
.button
:active
,
input
[
type
=
submit
]
:active
,
input
[
type
=
button
]
:active
{
background-image
:
url(../img/admin/nav-bg-reverse.gif)
;
background-position
:
top
;
}
.button
[
disabled
],
input
[
type
=
submit
][
disabled
],
input
[
type
=
button
][
disabled
]
{
background-image
:
url(../img/admin/nav-bg.gif)
;
background-position
:
bottom
;
opacity
:
0.4
;
}
.button.default
,
input
[
type
=
submit
]
.default
,
.submit-row
input
.default
{
border
:
2px
solid
#5b80b2
;
background
:
#7CA0C7
url(../img/admin/default-bg.gif)
bottom
repeat-x
;
font-weight
:
bold
;
color
:
white
;
float
:
right
;
}
.button.default
:active
,
input
[
type
=
submit
]
.default
:active
{
background-image
:
url(../img/admin/default-bg-reverse.gif)
;
background-position
:
top
;
}
.button
[
disabled
]
.default
,
input
[
type
=
submit
][
disabled
]
.default
,
input
[
type
=
button
][
disabled
]
.default
{
background-image
:
url(../img/admin/default-bg.gif)
;
background-position
:
bottom
;
opacity
:
0.4
;
}
/* MODULES */
.module
{
border
:
1px
solid
#ccc
;
margin-bottom
:
5px
;
background
:
white
;
}
.module
p
,
.module
ul
,
.module
h3
,
.module
h4
,
.module
dl
,
.module
pre
{
padding-left
:
10px
;
padding-right
:
10px
;
}
.module
blockquote
{
margin-left
:
12px
;
}
.module
ul
,
.module
ol
{
margin-left
:
1.5em
;
}
.module
h3
{
margin-top
:
.6em
;
}
.module
h2
,
.module
caption
,
.inline-group
h2
{
margin
:
0
;
padding
:
2px
5px
3px
5px
;
font-size
:
11px
;
text-align
:
left
;
font-weight
:
bold
;
background
:
#7CA0C7
url(../img/admin/default-bg.gif)
top
left
repeat-x
;
color
:
white
;
}
.module
table
{
border-collapse
:
collapse
;
}
/* MESSAGES & ERRORS */
ul
.messagelist
{
padding
:
0
0
5px
0
;
margin
:
0
;
}
ul
.messagelist
li
{
font-size
:
12px
;
display
:
block
;
padding
:
4px
5px
4px
25px
;
margin
:
0
0
3px
0
;
border-bottom
:
1px
solid
#ddd
;
color
:
#666
;
background
:
#ffc
url(../img/admin/icon_success.gif)
5px
.3em
no-repeat
;
}
ul
.messagelist
li
.warning
{
background-image
:
url(../img/admin/icon_alert.gif)
;
}
ul
.messagelist
li
.error
{
background-image
:
url(../img/admin/icon_error.gif)
;
}
.errornote
{
font-size
:
12px
!important
;
display
:
block
;
padding
:
4px
5px
4px
25px
;
margin
:
0
0
3px
0
;
border
:
1px
solid
red
;
color
:
red
;
background
:
#ffc
url(../img/admin/icon_error.gif)
5px
.3em
no-repeat
;
}
ul
.errorlist
{
margin
:
0
!important
;
padding
:
0
!important
;
}
.errorlist
li
{
font-size
:
12px
!important
;
display
:
block
;
padding
:
4px
5px
4px
25px
;
margin
:
0
0
3px
0
;
border
:
1px
solid
red
;
color
:
white
;
background
:
red
url(../img/admin/icon_alert.gif)
5px
.3em
no-repeat
;
}
.errorlist
li
a
{
color
:
white
;
text-decoration
:
underline
;
}
td
ul
.errorlist
{
margin
:
0
!important
;
padding
:
0
!important
;
}
td
ul
.errorlist
li
{
margin
:
0
!important
;
}
.errors
{
background
:
#ffc
;
}
.errors
input
,
.errors
select
,
.errors
textarea
{
border
:
1px
solid
red
;
}
div
.system-message
{
background
:
#ffc
;
margin
:
10px
;
padding
:
6px
8px
;
font-size
:
.8em
;
}
div
.system-message
p
.system-message-title
{
padding
:
4px
5px
4px
25px
;
margin
:
0
;
color
:
red
;
background
:
#ffc
url(../img/admin/icon_error.gif)
5px
.3em
no-repeat
;
}
.description
{
font-size
:
12px
;
padding
:
5px
0
0
12px
;
}
/* BREADCRUMBS */
div
.breadcrumbs
{
background
:
white
url(../img/admin/nav-bg-reverse.gif)
0
-10px
repeat-x
;
padding
:
2px
8px
3px
8px
;
font-size
:
11px
;
color
:
#999
;
border-top
:
1px
solid
white
;
border-bottom
:
1px
solid
#ccc
;
text-align
:
left
;
}
/* ACTION ICONS */
.addlink
{
padding-left
:
12px
;
background
:
url(../img/admin/icon_addlink.gif)
0
.2em
no-repeat
;
}
.changelink
{
padding-left
:
12px
;
background
:
url(../img/admin/icon_changelink.gif)
0
.2em
no-repeat
;
}
.deletelink
{
padding-left
:
12px
;
background
:
url(../img/admin/icon_deletelink.gif)
0
.25em
no-repeat
;
}
a
.deletelink
:link
,
a
.deletelink
:visited
{
color
:
#CC3434
;
}
a
.deletelink
:hover
{
color
:
#993333
;
}
/* OBJECT TOOLS */
.object-tools
{
font-size
:
10px
;
font-weight
:
bold
;
font-family
:
Arial
,
Helvetica
,
sans-serif
;
padding-left
:
0
;
float
:
right
;
position
:
relative
;
margin-top
:
-2.4em
;
margin-bottom
:
-2em
;
}
.form-row
.object-tools
{
margin-top
:
5px
;
margin-bottom
:
5px
;
float
:
none
;
height
:
2em
;
padding-left
:
3.5em
;
}
.object-tools
li
{
display
:
block
;
float
:
left
;
background
:
url(../img/admin/tool-left.gif)
0
0
no-repeat
;
padding
:
0
0
0
8px
;
margin-left
:
2px
;
height
:
16px
;
}
.object-tools
li
:hover
{
background
:
url(../img/admin/tool-left_over.gif)
0
0
no-repeat
;
}
.object-tools
a
:link
,
.object-tools
a
:visited
{
display
:
block
;
float
:
left
;
color
:
white
;
padding
:
.1em
14px
.1em
8px
;
height
:
14px
;
background
:
#999
url(../img/admin/tool-right.gif)
100%
0
no-repeat
;
}
.object-tools
a
:hover
,
.object-tools
li
:hover
a
{
background
:
#5b80b2
url(../img/admin/tool-right_over.gif)
100%
0
no-repeat
;
}
.object-tools
a
.viewsitelink
,
.object-tools
a
.golink
{
background
:
#999
url(../img/admin/tooltag-arrowright.gif)
top
right
no-repeat
;
padding-right
:
28px
;
}
.object-tools
a
.viewsitelink
:hover
,
.object-tools
a
.golink
:hover
{
background
:
#5b80b2
url(../img/admin/tooltag-arrowright_over.gif)
top
right
no-repeat
;
}
.object-tools
a
.addlink
{
background
:
#999
url(../img/admin/tooltag-add.gif)
top
right
no-repeat
;
padding-right
:
28px
;
}
.object-tools
a
.addlink
:hover
{
background
:
#5b80b2
url(../img/admin/tooltag-add_over.gif)
top
right
no-repeat
;
}
/* OBJECT HISTORY */
table
#change-history
{
width
:
100%
;
}
table
#change-history
tbody
th
{
width
:
16em
;
}
/* PAGE STRUCTURE */
#container
{
position
:
relative
;
width
:
100%
;
min-width
:
760px
;
padding
:
0
;
}
#content
{
margin
:
10px
15px
;
}
#header
{
width
:
100%
;
}
#content-main
{
float
:
left
;
width
:
100%
;
}
#content-related
{
float
:
right
;
width
:
18em
;
position
:
relative
;
margin-right
:
-19em
;
}
#footer
{
clear
:
both
;
padding
:
10px
;
}
/* COLUMN TYPES */
.colMS
{
margin-right
:
20em
!important
;
}
.colSM
{
margin-left
:
20em
!important
;
}
.colSM
#content-related
{
float
:
left
;
margin-right
:
0
;
margin-left
:
-19em
;
}
.colSM
#content-main
{
float
:
right
;
}
.popup
.colM
{
width
:
95%
;
}
.subcol
{
float
:
left
;
width
:
46%
;
margin-right
:
15px
;
}
.dashboard
#content
{
width
:
500px
;
}
/* HEADER */
#header
{
background
:
#417690
;
color
:
#ffc
;
overflow
:
hidden
;
}
#header
a
:link
,
#header
a
:visited
{
color
:
white
;
}
#header
a
:hover
{
text-decoration
:
underline
;
}
#branding
h1
{
padding
:
0
10px
;
font-size
:
18px
;
margin
:
8px
0
;
font-weight
:
normal
;
color
:
#f4f379
;
}
#branding
h2
{
padding
:
0
10px
;
font-size
:
14px
;
margin
:
-8px
0
8px
0
;
font-weight
:
normal
;
color
:
#ffc
;
}
#user-tools
{
position
:
absolute
;
top
:
0
;
right
:
0
;
padding
:
1.2em
10px
;
font-size
:
11px
;
text-align
:
right
;
}
/* SIDEBAR */
#content-related
h3
{
font-size
:
12px
;
color
:
#666
;
margin-bottom
:
3px
;
}
#content-related
h4
{
font-size
:
11px
;
}
#content-related
.module
h2
{
background
:
#eee
url(../img/admin/nav-bg.gif)
bottom
left
repeat-x
;
color
:
#666
;
}
/********************** admin 'forms.css' ************************/
/* FORM ROWS */
.form-row
{
overflow
:
hidden
;
padding
:
8px
12px
;
font-size
:
11px
;
border-bottom
:
1px
solid
#eee
;
}
.form-row
img
,
.form-row
input
{
vertical-align
:
middle
;
}
form
.form-row
p
{
padding-left
:
0
;
font-size
:
11px
;
}
/* FORM LABELS */
form
h4
{
margin
:
0
!important
;
padding
:
0
!important
;
border
:
none
!important
;
}
label
{
font-weight
:
normal
!important
;
color
:
#666
;
font-size
:
12px
;
}
.required
label
,
label
.required
{
font-weight
:
bold
!important
;
color
:
#333
!important
;
}
/* RADIO BUTTONS */
form
ul
.radiolist
li
{
list-style-type
:
none
;
}
form
ul
.radiolist
label
{
float
:
none
;
display
:
inline
;
}
form
ul
.inline
{
margin-left
:
0
;
padding
:
0
;
}
form
ul
.inline
li
{
float
:
left
;
padding-right
:
7px
;
}
/* ALIGNED FIELDSETS */
.aligned
label
{
display
:
block
;
padding
:
3px
10px
0
0
;
float
:
left
;
width
:
8em
;
}
.aligned
ul
label
{
display
:
inline
;
float
:
none
;
width
:
auto
;
}
.colMS
.aligned
.vLargeTextField
,
.colMS
.aligned
.vXMLLargeTextField
{
width
:
350px
;
}
form
.aligned
p
,
form
.aligned
ul
{
margin-left
:
7em
;
padding-left
:
30px
;
}
form
.aligned
table
p
{
margin-left
:
0
;
padding-left
:
0
;
}
form
.aligned
p
.help
{
padding-left
:
38px
;
}
.aligned
.vCheckboxLabel
{
float
:
none
!important
;
display
:
inline
;
padding-left
:
4px
;
}
.colM
.aligned
.vLargeTextField
,
.colM
.aligned
.vXMLLargeTextField
{
width
:
610px
;
}
.checkbox-row
p
.help
{
margin-left
:
0
;
padding-left
:
0
!important
;
}
fieldset
.field-box
{
float
:
left
;
margin-right
:
20px
;
}
/* WIDE FIELDSETS */
.wide
label
{
width
:
15em
!important
;
}
form
.wide
p
{
margin-left
:
15em
;
}
form
.wide
p
.help
{
padding-left
:
38px
;
}
.colM
fieldset
.wide
.vLargeTextField
,
.colM
fieldset
.wide
.vXMLLargeTextField
{
width
:
450px
;
}
/* COLLAPSED FIELDSETS */
fieldset
.collapsed
*
{
display
:
none
;
}
fieldset
.collapsed
h2
,
fieldset
.collapsed
{
display
:
block
!important
;
}
fieldset
.collapsed
h2
{
background-image
:
url(../img/admin/nav-bg.gif)
;
background-position
:
bottom
left
;
color
:
#999
;
}
fieldset
.collapsed
.collapse-toggle
{
background
:
transparent
;
display
:
inline
!important
;
}
/* MONOSPACE TEXTAREAS */
fieldset
.monospace
textarea
{
font-family
:
"Bitstream Vera Sans Mono"
,
Monaco
,
"Courier New"
,
Courier
,
monospace
;
}
/* SUBMIT ROW */
.submit-row
{
padding
:
5px
7px
;
text-align
:
right
;
background
:
white
url(../img/admin/nav-bg.gif)
0
100%
repeat-x
;
border
:
1px
solid
#ccc
;
margin
:
5px
0
;
overflow
:
hidden
;
}
.submit-row
input
{
margin
:
0
0
0
5px
;
}
.submit-row
p
{
margin
:
0.3em
;
}
.submit-row
p
.deletelink-box
{
float
:
left
;
}
.submit-row
.deletelink
{
background
:
url(../img/admin/icon_deletelink.gif)
0
50%
no-repeat
;
padding-left
:
14px
;
}
/* CUSTOM FORM FIELDS */
.vSelectMultipleField
{
vertical-align
:
top
!important
;
}
.vCheckboxField
{
border
:
none
;
}
.vDateField
,
.vTimeField
{
margin-right
:
2px
;
}
.vURLField
{
width
:
30em
;
}
.vLargeTextField
,
.vXMLLargeTextField
{
width
:
48em
;
}
.flatpages-flatpage
#id_content
{
height
:
40.2em
;
}
.module
table
.vPositiveSmallIntegerField
{
width
:
2.2em
;
}
.vTextField
{
width
:
20em
;
}
.vIntegerField
{
width
:
5em
;
}
.vForeignKeyRawIdAdminField
{
width
:
5em
;
}
/* INLINES */
.inline-group
{
padding
:
0
;
border
:
1px
solid
#ccc
;
margin
:
10px
0
;
}
.inline-group
.aligned
label
{
width
:
8em
;
}
.inline-related
{
position
:
relative
;
}
.inline-related
h3
{
margin
:
0
;
color
:
#666
;
padding
:
3px
5px
;
font-size
:
11px
;
background
:
#e1e1e1
url(../img/admin/nav-bg.gif)
top
left
repeat-x
;
border-bottom
:
1px
solid
#ddd
;
}
.inline-related
h3
span
.delete
{
float
:
right
;
}
.inline-related
h3
span
.delete
label
{
margin-left
:
2px
;
font-size
:
11px
;
}
.inline-related
fieldset
{
margin
:
0
;
background
:
#fff
;
border
:
none
;
}
.inline-related
fieldset
.module
h3
{
margin
:
0
;
padding
:
2px
5px
3px
5px
;
font-size
:
11px
;
text-align
:
left
;
font-weight
:
bold
;
background
:
#bcd
;
color
:
#fff
;
}
.inline-group
.tabular
fieldset
.module
{
border
:
none
;
border-bottom
:
1px
solid
#ddd
;
}
.inline-related.tabular
fieldset
.module
table
{
width
:
100%
;
}
.last-related
fieldset
{
border
:
none
;
}
.inline-group
.tabular
tr
.has_original
td
{
padding-top
:
2em
;
}
.inline-group
.tabular
tr
td
.original
{
padding
:
2px
0
0
0
;
width
:
0
;
_position
:
relative
;
}
.inline-group
.tabular
th
.original
{
width
:
0px
;
padding
:
0
;
}
.inline-group
.tabular
td
.original
p
{
position
:
absolute
;
left
:
0
;
height
:
1.1em
;
padding
:
2px
7px
;
overflow
:
hidden
;
font-size
:
9px
;
font-weight
:
bold
;
color
:
#666
;
_width
:
700px
;
}
.inline-group
ul
.tools
{
padding
:
0
;
margin
:
0
;
list-style
:
none
;
}
.inline-group
ul
.tools
li
{
display
:
inline
;
padding
:
0
5px
;
}
.inline-group
div
.add-row
,
.inline-group
.tabular
tr
.add-row
td
{
color
:
#666
;
padding
:
3px
5px
;
border-bottom
:
1px
solid
#ddd
;
background
:
#e1e1e1
url(../img/admin/nav-bg.gif)
top
left
repeat-x
;
}
.inline-group
.tabular
tr
.add-row
td
{
padding
:
4px
5px
3px
;
border-bottom
:
none
;
}
.inline-group
ul
.tools
a
.add
,
.inline-group
div
.add-row
a
,
.inline-group
.tabular
tr
.add-row
td
a
{
background
:
url(../img/admin/icon_addlink.gif)
0
50%
no-repeat
;
padding-left
:
14px
;
font-size
:
11px
;
outline
:
0
;
/* Remove dotted border around link */
}
.empty-form
{
display
:
none
;
}
/* IE7 specific bug fixes */
.submit-row
input
{
float
:
right
;
}
/* Overrides specific to REST framework */
#site-name
a
{
color
:
#F4F379
!important
;
}
.errorlist
{
display
:
inline
!important
;
}
.errorlist
li
{
display
:
inline
!important
;
background
:
white
!important
;
color
:
black
!important
;
border
:
0
!important
;
}
/* Custom styles */
.version
{
font-size
:
8px
;
}
djangorestframework/status.py
View file @
279fa0d3
...
@@ -5,7 +5,6 @@ See RFC 2616 - Sec 10: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
...
@@ -5,7 +5,6 @@ See RFC 2616 - Sec 10: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
Also see django.core.handlers.wsgi.STATUS_CODE_TEXT
Also see django.core.handlers.wsgi.STATUS_CODE_TEXT
"""
"""
# Verbose format
HTTP_100_CONTINUE
=
100
HTTP_100_CONTINUE
=
100
HTTP_101_SWITCHING_PROTOCOLS
=
101
HTTP_101_SWITCHING_PROTOCOLS
=
101
HTTP_200_OK
=
200
HTTP_200_OK
=
200
...
...
djangorestframework/templates/renderer.html
View file @
279fa0d3
{% load urlize_quoted_links %}{% load add_query_param %}
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
{% load urlize_quoted_links %}
{% load add_query_param %}
{% load static %}
<html
xmlns=
"http://www.w3.org/1999/xhtml"
>
<html
xmlns=
"http://www.w3.org/1999/xhtml"
>
<head>
<head>
<style>
<link
rel=
"stylesheet"
type=
"text/css"
href=
'{% get_static_prefix %}css/djangorestframework.css'
/>
/* Override some of the Django admin styling */
<title>
Django REST framework - {{ name }}
</title>
#site-name
a
{
color
:
#F4F379
!important
;}
</head>
.errorlist
{
display
:
inline
!important
}
.errorlist
li
{
display
:
inline
!important
;
background
:
white
!important
;
color
:
black
!important
;
border
:
0
!important
;}
/* Custom styles */
.version
{
font-size
:
8px
;}
</style>
{% if ADMIN_MEDIA_PREFIX %}
<link
rel=
"stylesheet"
type=
"text/css"
href=
'{{ADMIN_MEDIA_PREFIX}}css/base.css'
/>
<link
rel=
"stylesheet"
type=
"text/css"
href=
'{{ADMIN_MEDIA_PREFIX}}css/forms.css'
/>
{% else %}
<link
rel=
"stylesheet"
type=
"text/css"
href=
'{{STATIC_URL}}admin/css/base.css'
/>
<link
rel=
"stylesheet"
type=
"text/css"
href=
'{{STATIC_URL}}admin/css/forms.css'
/>
{% endif %}
<title>
Django REST framework - {{ name }}
</title>
</head>
<body>
<body>
<div
id=
"container"
>
<div
id=
"container"
>
...
@@ -34,7 +23,7 @@
...
@@ -34,7 +23,7 @@
<div
class=
"breadcrumbs"
>
<div
class=
"breadcrumbs"
>
{% for breadcrumb_name, breadcrumb_url in breadcrumblist %}
{% for breadcrumb_name, breadcrumb_url in breadcrumblist %}
<a
href=
"{{
breadcrumb_url}}"
>
{{breadcrumb_name
}}
</a>
{% if not forloop.last %}
›
{% endif %}
<a
href=
"{{
breadcrumb_url }}"
>
{{ breadcrumb_name
}}
</a>
{% if not forloop.last %}
›
{% endif %}
{% endfor %}
{% endfor %}
</div>
</div>
...
@@ -50,7 +39,7 @@
...
@@ -50,7 +39,7 @@
<div
class=
'content-main'
>
<div
class=
'content-main'
>
<h1>
{{ name }}
</h1>
<h1>
{{ name }}
</h1>
<p>
{
% if markeddown %}{% autoescape off %}{{ markeddown }}{% endautoescape %}{% else %}{{ description|linebreaksbr }}{% endif %
}
</p>
<p>
{
{ description }
}
</p>
<div
class=
'module'
>
<div
class=
'module'
>
<pre><b>
{{ response.status }} {{ response.status_text }}
</b>
{% autoescape off %}
<pre><b>
{{ response.status }} {{ response.status_text }}
</b>
{% autoescape off %}
{% for key, val in response.headers.items %}
<b>
{{ key }}:
</b>
{{ val|urlize_quoted_links }}
{% for key, val in response.headers.items %}
<b>
{{ key }}:
</b>
{{ val|urlize_quoted_links }}
...
...
djangorestframework/templates/renderer.txt
View file @
279fa0d3
{{ name }}
{
% autoescape off %}{
{ name }}
{{ description }}
{{ description }}
{% autoescape off %}
HTTP/1.0 {{ response.status }} {{ response.status_text }}
HTTP/1.0 {{ response.status }} {{ response.status_text }}
{% for key, val in response.headers.items %}{{ key }}: {{ val }}
{% for key, val in response.headers.items %}{{ key }}: {{ val }}
{% endfor %}
{% endfor %}
{{ content }}{% endautoescape %}
{{ content }}{% endautoescape %}
djangorestframework/templatetags/add_query_param.py
View file @
279fa0d3
...
@@ -5,7 +5,7 @@ register = Library()
...
@@ -5,7 +5,7 @@ register = Library()
def
add_query_param
(
url
,
param
):
def
add_query_param
(
url
,
param
):
(
key
,
sep
,
val
)
=
param
.
partition
(
'='
)
(
key
,
sep
,
val
)
=
param
.
partition
(
'='
)
return
unicode
(
URLObject
(
url
)
&
(
key
,
val
))
return
unicode
(
URLObject
.
parse
(
url
)
&
(
key
,
val
))
register
.
filter
(
'add_query_param'
,
add_query_param
)
register
.
filter
(
'add_query_param'
,
add_query_param
)
djangorestframework/tests/authentication.py
View file @
279fa0d3
...
@@ -11,7 +11,7 @@ import base64
...
@@ -11,7 +11,7 @@ import base64
class
MockView
(
View
):
class
MockView
(
View
):
permissions
=
(
permissions
.
IsAuthenticated
,
)
permissions
=
(
permissions
.
IsAuthenticated
,
)
def
post
(
self
,
request
):
def
post
(
self
,
request
):
return
{
'a'
:
1
,
'b'
:
2
,
'c'
:
3
}
return
{
'a'
:
1
,
'b'
:
2
,
'c'
:
3
}
...
@@ -74,24 +74,32 @@ class SessionAuthTests(TestCase):
...
@@ -74,24 +74,32 @@ class SessionAuthTests(TestCase):
self
.
csrf_client
.
logout
()
self
.
csrf_client
.
logout
()
def
test_post_form_session_auth_failing_csrf
(
self
):
def
test_post_form_session_auth_failing_csrf
(
self
):
"""Ensure POSTing form over session authentication without CSRF token fails."""
"""
Ensure POSTing form over session authentication without CSRF token fails.
"""
self
.
csrf_client
.
login
(
username
=
self
.
username
,
password
=
self
.
password
)
self
.
csrf_client
.
login
(
username
=
self
.
username
,
password
=
self
.
password
)
response
=
self
.
csrf_client
.
post
(
'/'
,
{
'example'
:
'example'
})
response
=
self
.
csrf_client
.
post
(
'/'
,
{
'example'
:
'example'
})
self
.
assertEqual
(
response
.
status_code
,
403
)
self
.
assertEqual
(
response
.
status_code
,
403
)
def
test_post_form_session_auth_passing
(
self
):
def
test_post_form_session_auth_passing
(
self
):
"""Ensure POSTing form over session authentication with logged in user and CSRF token passes."""
"""
Ensure POSTing form over session authentication with logged in user and CSRF token passes.
"""
self
.
non_csrf_client
.
login
(
username
=
self
.
username
,
password
=
self
.
password
)
self
.
non_csrf_client
.
login
(
username
=
self
.
username
,
password
=
self
.
password
)
response
=
self
.
non_csrf_client
.
post
(
'/'
,
{
'example'
:
'example'
})
response
=
self
.
non_csrf_client
.
post
(
'/'
,
{
'example'
:
'example'
})
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
status_code
,
200
)
def
test_put_form_session_auth_passing
(
self
):
def
test_put_form_session_auth_passing
(
self
):
"""Ensure PUTting form over session authentication with logged in user and CSRF token passes."""
"""
Ensure PUTting form over session authentication with logged in user and CSRF token passes.
"""
self
.
non_csrf_client
.
login
(
username
=
self
.
username
,
password
=
self
.
password
)
self
.
non_csrf_client
.
login
(
username
=
self
.
username
,
password
=
self
.
password
)
response
=
self
.
non_csrf_client
.
put
(
'/'
,
{
'example'
:
'example'
})
response
=
self
.
non_csrf_client
.
put
(
'/'
,
{
'example'
:
'example'
})
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
status_code
,
200
)
def
test_post_form_session_auth_failing
(
self
):
def
test_post_form_session_auth_failing
(
self
):
"""Ensure POSTing form over session authentication without logged in user fails."""
"""
Ensure POSTing form over session authentication without logged in user fails.
"""
response
=
self
.
csrf_client
.
post
(
'/'
,
{
'example'
:
'example'
})
response
=
self
.
csrf_client
.
post
(
'/'
,
{
'example'
:
'example'
})
self
.
assertEqual
(
response
.
status_code
,
403
)
self
.
assertEqual
(
response
.
status_code
,
403
)
djangorestframework/tests/description.py
View file @
279fa0d3
from
django.test
import
TestCase
from
django.test
import
TestCase
from
djangorestframework.views
import
View
from
djangorestframework.views
import
View
from
djangorestframework.compat
import
apply_markdown
from
djangorestframework.compat
import
apply_markdown
from
djangorestframework.utils.description
import
get_name
,
get_description
# We check that docstrings get nicely un-indented.
# We check that docstrings get nicely un-indented.
DESCRIPTION
=
"""an example docstring
DESCRIPTION
=
"""an example docstring
...
@@ -51,15 +50,15 @@ class TestViewNamesAndDescriptions(TestCase):
...
@@ -51,15 +50,15 @@ class TestViewNamesAndDescriptions(TestCase):
"""Ensure Resource names are based on the classname by default."""
"""Ensure Resource names are based on the classname by default."""
class
MockView
(
View
):
class
MockView
(
View
):
pass
pass
self
.
assertEquals
(
get_name
(
MockView
()
),
'Mock'
)
self
.
assertEquals
(
MockView
()
.
get_name
(
),
'Mock'
)
# This has been turned off now.
def
test_resource_name_can_be_set_explicitly
(
self
):
#def test_resource_name_can_be_set_explicitly(self):
"""Ensure Resource names can be set using the 'get_name' method."""
# """Ensure Resource names can be set using the 'name' class attribute."""
example
=
'Some Other Name'
# example = 'Some Other Name'
class
MockView
(
View
):
# class MockView(View
):
def
get_name
(
self
):
# name =
example
return
example
# self.assertEquals(get_name(MockView()
), example)
self
.
assertEquals
(
MockView
()
.
get_name
(
),
example
)
def
test_resource_description_uses_docstring_by_default
(
self
):
def
test_resource_description_uses_docstring_by_default
(
self
):
"""Ensure Resource names are based on the docstring by default."""
"""Ensure Resource names are based on the docstring by default."""
...
@@ -79,29 +78,30 @@ class TestViewNamesAndDescriptions(TestCase):
...
@@ -79,29 +78,30 @@ class TestViewNamesAndDescriptions(TestCase):
# hash style header #"""
# hash style header #"""
self
.
assertEquals
(
get_description
(
MockView
()),
DESCRIPTION
)
self
.
assertEquals
(
MockView
()
.
get_description
(),
DESCRIPTION
)
# This has been turned off now
#def test_resource_description_can_be_set_explicitly(self):
# """Ensure Resource descriptions can be set using the 'description' class attribute."""
# example = 'Some other description'
# class MockView(View):
# """docstring"""
# description = example
# self.assertEquals(get_description(MockView()), example)
#def test_resource_description_does_not_require_docstring(self):
def
test_resource_description_can_be_set_explicitly
(
self
):
# """Ensure that empty docstrings do not affect the Resource's description if it has been set using the 'description' class attribute."""
"""Ensure Resource descriptions can be set using the 'get_description' method."""
# example = 'Some other description'
example
=
'Some other description'
# class MockView(View):
class
MockView
(
View
):
# description = example
"""docstring"""
# self.assertEquals(get_description(MockView()), example)
def
get_description
(
self
):
return
example
self
.
assertEquals
(
MockView
()
.
get_description
(),
example
)
def
test_resource_description_does_not_require_docstring
(
self
):
"""Ensure that empty docstrings do not affect the Resource's description if it has been set using the 'get_description' method."""
example
=
'Some other description'
class
MockView
(
View
):
def
get_description
(
self
):
return
example
self
.
assertEquals
(
MockView
()
.
get_description
(),
example
)
def
test_resource_description_can_be_empty
(
self
):
def
test_resource_description_can_be_empty
(
self
):
"""Ensure that if a resource has no doctring or 'description' class attribute, then it's description is the empty string"""
"""Ensure that if a resource has no doctring or 'description' class attribute, then it's description is the empty string
.
"""
class
MockView
(
View
):
class
MockView
(
View
):
pass
pass
self
.
assertEquals
(
get_description
(
MockView
()
),
''
)
self
.
assertEquals
(
MockView
()
.
get_description
(
),
''
)
def
test_markdown
(
self
):
def
test_markdown
(
self
):
"""Ensure markdown to HTML works as expected"""
"""Ensure markdown to HTML works as expected"""
...
...
djangorestframework/tests/mixins.py
View file @
279fa0d3
...
@@ -30,7 +30,7 @@ class TestModelRead(TestModelsTestCase):
...
@@ -30,7 +30,7 @@ class TestModelRead(TestModelsTestCase):
mixin
=
ReadModelMixin
()
mixin
=
ReadModelMixin
()
mixin
.
resource
=
GroupResource
mixin
.
resource
=
GroupResource
response
=
mixin
.
get
(
request
,
group
.
id
)
response
=
mixin
.
get
(
request
,
id
=
group
.
id
)
self
.
assertEquals
(
group
.
name
,
response
.
name
)
self
.
assertEquals
(
group
.
name
,
response
.
name
)
def
test_read_404
(
self
):
def
test_read_404
(
self
):
...
@@ -41,8 +41,7 @@ class TestModelRead(TestModelsTestCase):
...
@@ -41,8 +41,7 @@ class TestModelRead(TestModelsTestCase):
mixin
=
ReadModelMixin
()
mixin
=
ReadModelMixin
()
mixin
.
resource
=
GroupResource
mixin
.
resource
=
GroupResource
with
self
.
assertRaises
(
ErrorResponse
):
self
.
assertRaises
(
ErrorResponse
,
mixin
.
get
,
request
,
id
=
12345
)
response
=
mixin
.
get
(
request
,
12345
)
class
TestModelCreation
(
TestModelsTestCase
):
class
TestModelCreation
(
TestModelsTestCase
):
...
...
djangorestframework/tests/renderers.py
View file @
279fa0d3
import
re
from
django.conf.urls.defaults
import
patterns
,
url
from
django.conf.urls.defaults
import
patterns
,
url
from
django.test
import
TestCase
from
django.test
import
TestCase
...
@@ -174,6 +176,12 @@ class RendererIntegrationTests(TestCase):
...
@@ -174,6 +176,12 @@ class RendererIntegrationTests(TestCase):
_flat_repr
=
'{"foo": ["bar", "baz"]}'
_flat_repr
=
'{"foo": ["bar", "baz"]}'
_indented_repr
=
'{
\n
"foo": [
\n
"bar",
\n
"baz"
\n
]
\n
}'
_indented_repr
=
'{
\n
"foo": [
\n
"bar",
\n
"baz"
\n
]
\n
}'
def
strip_trailing_whitespace
(
content
):
"""
Seems to be some inconsistencies re. trailing whitespace with
different versions of the json lib.
"""
return
re
.
sub
(
' +
\n
'
,
'
\n
'
,
content
)
class
JSONRendererTests
(
TestCase
):
class
JSONRendererTests
(
TestCase
):
"""
"""
...
@@ -187,6 +195,7 @@ class JSONRendererTests(TestCase):
...
@@ -187,6 +195,7 @@ class JSONRendererTests(TestCase):
obj
=
{
'foo'
:
[
'bar'
,
'baz'
]}
obj
=
{
'foo'
:
[
'bar'
,
'baz'
]}
renderer
=
JSONRenderer
(
None
)
renderer
=
JSONRenderer
(
None
)
content
=
renderer
.
render
(
obj
,
'application/json'
)
content
=
renderer
.
render
(
obj
,
'application/json'
)
# Fix failing test case which depends on version of JSON library.
self
.
assertEquals
(
content
,
_flat_repr
)
self
.
assertEquals
(
content
,
_flat_repr
)
def
test_with_content_type_args
(
self
):
def
test_with_content_type_args
(
self
):
...
@@ -196,7 +205,7 @@ class JSONRendererTests(TestCase):
...
@@ -196,7 +205,7 @@ class JSONRendererTests(TestCase):
obj
=
{
'foo'
:
[
'bar'
,
'baz'
]}
obj
=
{
'foo'
:
[
'bar'
,
'baz'
]}
renderer
=
JSONRenderer
(
None
)
renderer
=
JSONRenderer
(
None
)
content
=
renderer
.
render
(
obj
,
'application/json; indent=2'
)
content
=
renderer
.
render
(
obj
,
'application/json; indent=2'
)
self
.
assertEquals
(
content
,
_indented_repr
)
self
.
assertEquals
(
strip_trailing_whitespace
(
content
)
,
_indented_repr
)
def
test_render_and_parse
(
self
):
def
test_render_and_parse
(
self
):
"""
"""
...
...
djangorestframework/tests/validators.py
View file @
279fa0d3
from
django
import
forms
from
django
import
forms
from
django.db
import
models
from
django.db
import
models
from
django.test
import
TestCase
from
django.test
import
TestCase
from
djangorestframework.compat
import
RequestFactory
from
djangorestframework.resources
import
FormResource
,
ModelResource
from
djangorestframework.resources
import
Resource
,
FormResource
,
ModelResource
from
djangorestframework.response
import
ErrorResponse
from
djangorestframework.response
import
ErrorResponse
from
djangorestframework.views
import
View
from
djangorestframework.views
import
View
from
djangorestframework.resources
import
Resource
class
TestDisabledValidations
(
TestCase
):
class
TestDisabledValidations
(
TestCase
):
...
@@ -22,7 +19,7 @@ class TestDisabledValidations(TestCase):
...
@@ -22,7 +19,7 @@ class TestDisabledValidations(TestCase):
resource
=
DisabledFormResource
resource
=
DisabledFormResource
view
=
MockView
()
view
=
MockView
()
content
=
{
'qwerty'
:
'uiop'
}
content
=
{
'qwerty'
:
'uiop'
}
self
.
assertEqual
(
FormResource
(
view
)
.
validate_request
(
content
,
None
),
content
)
self
.
assertEqual
(
FormResource
(
view
)
.
validate_request
(
content
,
None
),
content
)
def
test_disabled_form_validator_get_bound_form_returns_none
(
self
):
def
test_disabled_form_validator_get_bound_form_returns_none
(
self
):
...
@@ -35,10 +32,9 @@ class TestDisabledValidations(TestCase):
...
@@ -35,10 +32,9 @@ class TestDisabledValidations(TestCase):
resource
=
DisabledFormResource
resource
=
DisabledFormResource
view
=
MockView
()
view
=
MockView
()
content
=
{
'qwerty'
:
'uiop'
}
content
=
{
'qwerty'
:
'uiop'
}
self
.
assertEqual
(
FormResource
(
view
)
.
get_bound_form
(
content
),
None
)
self
.
assertEqual
(
FormResource
(
view
)
.
get_bound_form
(
content
),
None
)
def
test_disabled_model_form_validator_returns_content_unchanged
(
self
):
def
test_disabled_model_form_validator_returns_content_unchanged
(
self
):
"""If the view's form is None and does not have a Resource with a model set then
"""If the view's form is None and does not have a Resource with a model set then
ModelFormValidator(view).validate_request(content, None) should just return the content unmodified."""
ModelFormValidator(view).validate_request(content, None) should just return the content unmodified."""
...
@@ -47,8 +43,8 @@ class TestDisabledValidations(TestCase):
...
@@ -47,8 +43,8 @@ class TestDisabledValidations(TestCase):
resource
=
ModelResource
resource
=
ModelResource
view
=
DisabledModelFormView
()
view
=
DisabledModelFormView
()
content
=
{
'qwerty'
:
'uiop'
}
content
=
{
'qwerty'
:
'uiop'
}
self
.
assertEqual
(
ModelResource
(
view
)
.
get_bound_form
(
content
),
None
)
#
self
.
assertEqual
(
ModelResource
(
view
)
.
get_bound_form
(
content
),
None
)
def
test_disabled_model_form_validator_get_bound_form_returns_none
(
self
):
def
test_disabled_model_form_validator_get_bound_form_returns_none
(
self
):
"""If the form attribute is None on FormValidatorMixin then get_bound_form(content) should just return None."""
"""If the form attribute is None on FormValidatorMixin then get_bound_form(content) should just return None."""
...
@@ -56,9 +52,10 @@ class TestDisabledValidations(TestCase):
...
@@ -56,9 +52,10 @@ class TestDisabledValidations(TestCase):
resource
=
ModelResource
resource
=
ModelResource
view
=
DisabledModelFormView
()
view
=
DisabledModelFormView
()
content
=
{
'qwerty'
:
'uiop'
}
content
=
{
'qwerty'
:
'uiop'
}
self
.
assertEqual
(
ModelResource
(
view
)
.
get_bound_form
(
content
),
None
)
self
.
assertEqual
(
ModelResource
(
view
)
.
get_bound_form
(
content
),
None
)
class
TestNonFieldErrors
(
TestCase
):
class
TestNonFieldErrors
(
TestCase
):
"""Tests against form validation errors caused by non-field errors. (eg as might be caused by some custom form validation)"""
"""Tests against form validation errors caused by non-field errors. (eg as might be caused by some custom form validation)"""
...
@@ -72,7 +69,7 @@ class TestNonFieldErrors(TestCase):
...
@@ -72,7 +69,7 @@ class TestNonFieldErrors(TestCase):
def
clean
(
self
):
def
clean
(
self
):
if
'field1'
in
self
.
cleaned_data
and
'field2'
in
self
.
cleaned_data
:
if
'field1'
in
self
.
cleaned_data
and
'field2'
in
self
.
cleaned_data
:
raise
forms
.
ValidationError
(
self
.
ERROR_TEXT
)
raise
forms
.
ValidationError
(
self
.
ERROR_TEXT
)
return
self
.
cleaned_data
#pragma: no cover
return
self
.
cleaned_data
class
MockResource
(
FormResource
):
class
MockResource
(
FormResource
):
form
=
MockForm
form
=
MockForm
...
@@ -87,7 +84,7 @@ class TestNonFieldErrors(TestCase):
...
@@ -87,7 +84,7 @@ class TestNonFieldErrors(TestCase):
except
ErrorResponse
,
exc
:
except
ErrorResponse
,
exc
:
self
.
assertEqual
(
exc
.
response
.
raw_content
,
{
'errors'
:
[
MockForm
.
ERROR_TEXT
]})
self
.
assertEqual
(
exc
.
response
.
raw_content
,
{
'errors'
:
[
MockForm
.
ERROR_TEXT
]})
else
:
else
:
self
.
fail
(
'ErrorResponse was not raised'
)
#pragma: no cover
self
.
fail
(
'ErrorResponse was not raised'
)
class
TestFormValidation
(
TestCase
):
class
TestFormValidation
(
TestCase
):
...
@@ -115,10 +112,9 @@ class TestFormValidation(TestCase):
...
@@ -115,10 +112,9 @@ class TestFormValidation(TestCase):
self
.
MockFormView
=
MockFormView
self
.
MockFormView
=
MockFormView
self
.
MockModelFormView
=
MockModelFormView
self
.
MockModelFormView
=
MockModelFormView
def
validation_returns_content_unchanged_if_already_valid_and_clean
(
self
,
validator
):
def
validation_returns_content_unchanged_if_already_valid_and_clean
(
self
,
validator
):
"""If the content is already valid and clean then validate(content) should just return the content unmodified."""
"""If the content is already valid and clean then validate(content) should just return the content unmodified."""
content
=
{
'qwerty'
:
'uiop'
}
content
=
{
'qwerty'
:
'uiop'
}
self
.
assertEqual
(
validator
.
validate_request
(
content
,
None
),
content
)
self
.
assertEqual
(
validator
.
validate_request
(
content
,
None
),
content
)
def
validation_failure_raises_response_exception
(
self
,
validator
):
def
validation_failure_raises_response_exception
(
self
,
validator
):
...
@@ -143,7 +139,9 @@ class TestFormValidation(TestCase):
...
@@ -143,7 +139,9 @@ class TestFormValidation(TestCase):
raise errors on unexpected request data"""
raise errors on unexpected request data"""
content
=
{
'qwerty'
:
'uiop'
,
'extra'
:
'extra'
}
content
=
{
'qwerty'
:
'uiop'
,
'extra'
:
'extra'
}
validator
.
allow_unknown_form_fields
=
True
validator
.
allow_unknown_form_fields
=
True
self
.
assertDictEqual
({
'qwerty'
:
u'uiop'
},
validator
.
validate_request
(
content
,
None
),
"Resource didn't accept unknown fields."
)
self
.
assertEqual
({
'qwerty'
:
u'uiop'
},
validator
.
validate_request
(
content
,
None
),
"Resource didn't accept unknown fields."
)
validator
.
allow_unknown_form_fields
=
False
validator
.
allow_unknown_form_fields
=
False
def
validation_does_not_require_extra_fields_if_explicitly_set
(
self
,
validator
):
def
validation_does_not_require_extra_fields_if_explicitly_set
(
self
,
validator
):
...
@@ -159,7 +157,7 @@ class TestFormValidation(TestCase):
...
@@ -159,7 +157,7 @@ class TestFormValidation(TestCase):
except
ErrorResponse
,
exc
:
except
ErrorResponse
,
exc
:
self
.
assertEqual
(
exc
.
response
.
raw_content
,
{
'field_errors'
:
{
'qwerty'
:
[
'This field is required.'
]}})
self
.
assertEqual
(
exc
.
response
.
raw_content
,
{
'field_errors'
:
{
'qwerty'
:
[
'This field is required.'
]}})
else
:
else
:
self
.
fail
(
'ResourceException was not raised'
)
#pragma: no cover
self
.
fail
(
'ResourceException was not raised'
)
def
validation_failed_due_to_field_error_returns_appropriate_message
(
self
,
validator
):
def
validation_failed_due_to_field_error_returns_appropriate_message
(
self
,
validator
):
"""If validation fails due to a field error, ensure the response contains a single field error"""
"""If validation fails due to a field error, ensure the response contains a single field error"""
...
@@ -169,7 +167,7 @@ class TestFormValidation(TestCase):
...
@@ -169,7 +167,7 @@ class TestFormValidation(TestCase):
except
ErrorResponse
,
exc
:
except
ErrorResponse
,
exc
:
self
.
assertEqual
(
exc
.
response
.
raw_content
,
{
'field_errors'
:
{
'qwerty'
:
[
'This field is required.'
]}})
self
.
assertEqual
(
exc
.
response
.
raw_content
,
{
'field_errors'
:
{
'qwerty'
:
[
'This field is required.'
]}})
else
:
else
:
self
.
fail
(
'ResourceException was not raised'
)
#pragma: no cover
self
.
fail
(
'ResourceException was not raised'
)
def
validation_failed_due_to_invalid_field_returns_appropriate_message
(
self
,
validator
):
def
validation_failed_due_to_invalid_field_returns_appropriate_message
(
self
,
validator
):
"""If validation fails due to an invalid field, ensure the response contains a single field error"""
"""If validation fails due to an invalid field, ensure the response contains a single field error"""
...
@@ -179,7 +177,7 @@ class TestFormValidation(TestCase):
...
@@ -179,7 +177,7 @@ class TestFormValidation(TestCase):
except
ErrorResponse
,
exc
:
except
ErrorResponse
,
exc
:
self
.
assertEqual
(
exc
.
response
.
raw_content
,
{
'field_errors'
:
{
'extra'
:
[
'This field does not exist.'
]}})
self
.
assertEqual
(
exc
.
response
.
raw_content
,
{
'field_errors'
:
{
'extra'
:
[
'This field does not exist.'
]}})
else
:
else
:
self
.
fail
(
'ResourceException was not raised'
)
#pragma: no cover
self
.
fail
(
'ResourceException was not raised'
)
def
validation_failed_due_to_multiple_errors_returns_appropriate_message
(
self
,
validator
):
def
validation_failed_due_to_multiple_errors_returns_appropriate_message
(
self
,
validator
):
"""If validation for multiple reasons, ensure the response contains each error"""
"""If validation for multiple reasons, ensure the response contains each error"""
...
@@ -190,7 +188,7 @@ class TestFormValidation(TestCase):
...
@@ -190,7 +188,7 @@ class TestFormValidation(TestCase):
self
.
assertEqual
(
exc
.
response
.
raw_content
,
{
'field_errors'
:
{
'qwerty'
:
[
'This field is required.'
],
self
.
assertEqual
(
exc
.
response
.
raw_content
,
{
'field_errors'
:
{
'qwerty'
:
[
'This field is required.'
],
'extra'
:
[
'This field does not exist.'
]}})
'extra'
:
[
'This field does not exist.'
]}})
else
:
else
:
self
.
fail
(
'ResourceException was not raised'
)
#pragma: no cover
self
.
fail
(
'ResourceException was not raised'
)
# Tests on FormResource
# Tests on FormResource
...
@@ -209,7 +207,7 @@ class TestFormValidation(TestCase):
...
@@ -209,7 +207,7 @@ class TestFormValidation(TestCase):
def
test_validation_allows_extra_fields_if_explicitly_set
(
self
):
def
test_validation_allows_extra_fields_if_explicitly_set
(
self
):
validator
=
self
.
MockFormResource
(
self
.
MockFormView
())
validator
=
self
.
MockFormResource
(
self
.
MockFormView
())
self
.
validation_allows_extra_fields_if_explicitly_set
(
validator
)
self
.
validation_allows_extra_fields_if_explicitly_set
(
validator
)
def
test_validation_allows_unknown_fields_if_explicitly_allowed
(
self
):
def
test_validation_allows_unknown_fields_if_explicitly_allowed
(
self
):
validator
=
self
.
MockFormResource
(
self
.
MockFormView
())
validator
=
self
.
MockFormResource
(
self
.
MockFormView
())
self
.
validation_allows_unknown_fields_if_explicitly_allowed
(
validator
)
self
.
validation_allows_unknown_fields_if_explicitly_allowed
(
validator
)
...
@@ -294,22 +292,21 @@ class TestModelFormValidator(TestCase):
...
@@ -294,22 +292,21 @@ class TestModelFormValidator(TestCase):
self
.
validator
=
MockResource
(
MockView
)
self
.
validator
=
MockResource
(
MockView
)
def
test_property_fields_are_allowed_on_model_forms
(
self
):
def
test_property_fields_are_allowed_on_model_forms
(
self
):
"""Validation on ModelForms may include property fields that exist on the Model to be included in the input."""
"""Validation on ModelForms may include property fields that exist on the Model to be included in the input."""
content
=
{
'qwerty'
:
'example'
,
'uiop'
:
'example'
,
'readonly'
:
'read only'
}
content
=
{
'qwerty'
:
'example'
,
'uiop'
:
'example'
,
'readonly'
:
'read only'
}
self
.
assertEqual
(
self
.
validator
.
validate_request
(
content
,
None
),
content
)
self
.
assertEqual
(
self
.
validator
.
validate_request
(
content
,
None
),
content
)
def
test_property_fields_are_not_required_on_model_forms
(
self
):
def
test_property_fields_are_not_required_on_model_forms
(
self
):
"""Validation on ModelForms does not require property fields that exist on the Model to be included in the input."""
"""Validation on ModelForms does not require property fields that exist on the Model to be included in the input."""
content
=
{
'qwerty'
:
'example'
,
'uiop'
:
'example'
}
content
=
{
'qwerty'
:
'example'
,
'uiop'
:
'example'
}
self
.
assertEqual
(
self
.
validator
.
validate_request
(
content
,
None
),
content
)
self
.
assertEqual
(
self
.
validator
.
validate_request
(
content
,
None
),
content
)
def
test_extra_fields_not_allowed_on_model_forms
(
self
):
def
test_extra_fields_not_allowed_on_model_forms
(
self
):
"""If some (otherwise valid) content includes fields that are not in the form then validation should fail.
"""If some (otherwise valid) content includes fields that are not in the form then validation should fail.
It might be okay on normal form submission, but for Web APIs we oughta get strict, as it'll help show up
It might be okay on normal form submission, but for Web APIs we oughta get strict, as it'll help show up
broken clients more easily (eg submitting content with a misnamed field)"""
broken clients more easily (eg submitting content with a misnamed field)"""
content
=
{
'qwerty'
:
'example'
,
'uiop'
:
'example'
,
'readonly'
:
'read only'
,
'extra'
:
'extra'
}
content
=
{
'qwerty'
:
'example'
,
'uiop'
:
'example'
,
'readonly'
:
'read only'
,
'extra'
:
'extra'
}
self
.
assertRaises
(
ErrorResponse
,
self
.
validator
.
validate_request
,
content
,
None
)
self
.
assertRaises
(
ErrorResponse
,
self
.
validator
.
validate_request
,
content
,
None
)
def
test_validate_requires_fields_on_model_forms
(
self
):
def
test_validate_requires_fields_on_model_forms
(
self
):
...
@@ -321,10 +318,8 @@ class TestModelFormValidator(TestCase):
...
@@ -321,10 +318,8 @@ class TestModelFormValidator(TestCase):
def
test_validate_does_not_require_blankable_fields_on_model_forms
(
self
):
def
test_validate_does_not_require_blankable_fields_on_model_forms
(
self
):
"""Test standard ModelForm validation behaviour - fields with blank=True are not required."""
"""Test standard ModelForm validation behaviour - fields with blank=True are not required."""
content
=
{
'qwerty'
:
'example'
,
'readonly'
:
'read only'
}
content
=
{
'qwerty'
:
'example'
,
'readonly'
:
'read only'
}
self
.
validator
.
validate_request
(
content
,
None
)
self
.
validator
.
validate_request
(
content
,
None
)
def
test_model_form_validator_uses_model_forms
(
self
):
def
test_model_form_validator_uses_model_forms
(
self
):
self
.
assertTrue
(
isinstance
(
self
.
validator
.
get_bound_form
(),
forms
.
ModelForm
))
self
.
assertTrue
(
isinstance
(
self
.
validator
.
get_bound_form
(),
forms
.
ModelForm
))
djangorestframework/utils/breadcrumbs.py
View file @
279fa0d3
from
django.core.urlresolvers
import
resolve
from
django.core.urlresolvers
import
resolve
from
djangorestframework.utils.description
import
get_name
def
get_breadcrumbs
(
url
):
def
get_breadcrumbs
(
url
):
"""Given a url returns a list of breadcrumbs, which are each a tuple of (name, url)."""
"""Given a url returns a list of breadcrumbs, which are each a tuple of (name, url)."""
...
@@ -17,7 +15,7 @@ def get_breadcrumbs(url):
...
@@ -17,7 +15,7 @@ def get_breadcrumbs(url):
else
:
else
:
# Check if this is a REST framework view, and if so add it to the breadcrumbs
# Check if this is a REST framework view, and if so add it to the breadcrumbs
if
isinstance
(
getattr
(
view
,
'cls_instance'
,
None
),
View
):
if
isinstance
(
getattr
(
view
,
'cls_instance'
,
None
),
View
):
breadcrumbs_list
.
insert
(
0
,
(
get_name
(
view
),
url
))
breadcrumbs_list
.
insert
(
0
,
(
view
.
cls_instance
.
get_name
(
),
url
))
if
url
==
''
:
if
url
==
''
:
# All done
# All done
...
...
djangorestframework/utils/description.py
deleted
100644 → 0
View file @
152c385f
"""
Get a descriptive name and description for a view.
"""
import
re
from
djangorestframework.resources
import
Resource
,
FormResource
,
ModelResource
# These a a bit Grungy, but they do the job.
def
get_name
(
view
):
"""
Return a name for the view.
If view has a name attribute, use that, otherwise use the view's class name, with 'CamelCaseNames' converted to 'Camel Case Names'.
"""
# If we're looking up the name of a view callable, as found by reverse,
# grok the class instance that we stored when as_view was called.
if
getattr
(
view
,
'cls_instance'
,
None
):
view
=
view
.
cls_instance
# If this view has a resource that's been overridden, then use that resource for the name
if
getattr
(
view
,
'resource'
,
None
)
not
in
(
None
,
Resource
,
FormResource
,
ModelResource
):
name
=
view
.
resource
.
__name__
# Chomp of any non-descriptive trailing part of the resource class name
if
name
.
endswith
(
'Resource'
)
and
name
!=
'Resource'
:
name
=
name
[:
-
len
(
'Resource'
)]
# If the view has a descriptive suffix, eg '*** List', '*** Instance'
if
getattr
(
view
,
'_suffix'
,
None
):
name
+=
view
.
_suffix
# Otherwise if it's a function view use the function's name
elif
getattr
(
view
,
'__name__'
,
None
)
is
not
None
:
name
=
view
.
__name__
# If it's a view class with no resource then grok the name from the class name
elif
getattr
(
view
,
'__class__'
,
None
)
is
not
None
:
name
=
view
.
__class__
.
__name__
# Chomp of any non-descriptive trailing part of the view class name
if
name
.
endswith
(
'View'
)
and
name
!=
'View'
:
name
=
name
[:
-
len
(
'View'
)]
# I ain't got nuthin fo' ya
else
:
return
''
return
re
.
sub
(
'(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))'
,
'
\\
1'
,
name
)
.
strip
()
def
get_description
(
view
):
"""
Provide a description for the view.
By default this is the view's docstring with nice unindention applied.
"""
# If we're looking up the name of a view callable, as found by reverse,
# grok the class instance that we stored when as_view was called.
if
getattr
(
view
,
'cls_instance'
,
None
):
view
=
view
.
cls_instance
# If this view has a resource that's been overridden, then use the resource's doctring
if
getattr
(
view
,
'resource'
,
None
)
not
in
(
None
,
Resource
,
FormResource
,
ModelResource
):
doc
=
view
.
resource
.
__doc__
# Otherwise use the view doctring
elif
getattr
(
view
,
'__doc__'
,
None
):
doc
=
view
.
__doc__
# I ain't got nuthin fo' ya
else
:
return
''
if
not
doc
:
return
''
whitespace_counts
=
[
len
(
line
)
-
len
(
line
.
lstrip
(
' '
))
for
line
in
doc
.
splitlines
()[
1
:]
if
line
.
lstrip
()]
# unindent the docstring if needed
if
whitespace_counts
:
whitespace_pattern
=
'^'
+
(
' '
*
min
(
whitespace_counts
))
return
re
.
sub
(
re
.
compile
(
whitespace_pattern
,
re
.
MULTILINE
),
''
,
doc
)
# otherwise return it as-is
return
doc
djangorestframework/views.py
View file @
279fa0d3
...
@@ -5,15 +5,17 @@ be subclassing in your implementation.
...
@@ -5,15 +5,17 @@ be subclassing in your implementation.
By setting or modifying class attributes on your view, you change it's predefined behaviour.
By setting or modifying class attributes on your view, you change it's predefined behaviour.
"""
"""
import
re
from
django.core.urlresolvers
import
set_script_prefix
,
get_script_prefix
from
django.core.urlresolvers
import
set_script_prefix
,
get_script_prefix
from
django.http
import
HttpResponse
from
django.http
import
HttpResponse
from
django.utils.html
import
escape
from
django.utils.safestring
import
mark_safe
from
django.views.decorators.csrf
import
csrf_exempt
from
django.views.decorators.csrf
import
csrf_exempt
from
djangorestframework.compat
import
View
as
DjangoView
from
djangorestframework.compat
import
View
as
DjangoView
,
apply_markdown
from
djangorestframework.response
import
Response
,
ErrorResponse
from
djangorestframework.response
import
Response
,
ErrorResponse
from
djangorestframework.mixins
import
*
from
djangorestframework.mixins
import
*
from
djangorestframework
import
resources
,
renderers
,
parsers
,
authentication
,
permissions
,
status
from
djangorestframework
import
resources
,
renderers
,
parsers
,
authentication
,
permissions
,
status
from
djangorestframework.utils.description
import
get_name
,
get_description
__all__
=
(
__all__
=
(
...
@@ -25,6 +27,48 @@ __all__ = (
...
@@ -25,6 +27,48 @@ __all__ = (
)
)
def
_remove_trailing_string
(
content
,
trailing
):
"""
Strip trailing component `trailing` from `content` if it exists.
Used when generating names from view/resource classes.
"""
if
content
.
endswith
(
trailing
)
and
content
!=
trailing
:
return
content
[:
-
len
(
trailing
)]
return
content
def
_remove_leading_indent
(
content
):
"""
Remove leading indent from a block of text.
Used when generating descriptions from docstrings.
"""
whitespace_counts
=
[
len
(
line
)
-
len
(
line
.
lstrip
(
' '
))
for
line
in
content
.
splitlines
()[
1
:]
if
line
.
lstrip
()]
# unindent the content if needed
if
whitespace_counts
:
whitespace_pattern
=
'^'
+
(
' '
*
min
(
whitespace_counts
))
return
re
.
sub
(
re
.
compile
(
whitespace_pattern
,
re
.
MULTILINE
),
''
,
content
)
return
content
def
_camelcase_to_spaces
(
content
):
"""
Translate 'CamelCaseNames' to 'Camel Case Names'.
Used when generating names from view/resource classes.
"""
camelcase_boundry
=
'(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))'
return
re
.
sub
(
camelcase_boundry
,
'
\\
1'
,
content
)
.
strip
()
_resource_classes
=
(
None
,
resources
.
Resource
,
resources
.
FormResource
,
resources
.
ModelResource
)
class
View
(
ResourceMixin
,
RequestMixin
,
ResponseMixin
,
AuthMixin
,
DjangoView
):
class
View
(
ResourceMixin
,
RequestMixin
,
ResponseMixin
,
AuthMixin
,
DjangoView
):
"""
"""
Handles incoming requests and maps them to REST operations.
Handles incoming requests and maps them to REST operations.
...
@@ -48,7 +92,7 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
...
@@ -48,7 +92,7 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
"""
"""
authentication
=
(
authentication
.
UserLoggedInAuthentication
,
authentication
=
(
authentication
.
UserLoggedInAuthentication
,
authentication
.
BasicAuthentication
)
authentication
.
BasicAuthentication
)
"""
"""
List of all authenticating methods to attempt.
List of all authenticating methods to attempt.
"""
"""
...
@@ -76,6 +120,54 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
...
@@ -76,6 +120,54 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
"""
"""
return
[
method
.
upper
()
for
method
in
self
.
http_method_names
if
hasattr
(
self
,
method
)]
return
[
method
.
upper
()
for
method
in
self
.
http_method_names
if
hasattr
(
self
,
method
)]
def
get_name
(
self
):
"""
Return the resource or view class name for use as this view's name.
Override to customize.
"""
# If this view has a resource that's been overridden, then use that resource for the name
if
getattr
(
self
,
'resource'
,
None
)
not
in
_resource_classes
:
name
=
self
.
resource
.
__name__
name
=
_remove_trailing_string
(
name
,
'Resource'
)
name
+=
getattr
(
self
,
'_suffix'
,
''
)
# If it's a view class with no resource then grok the name from the class name
else
:
name
=
self
.
__class__
.
__name__
name
=
_remove_trailing_string
(
name
,
'View'
)
return
_camelcase_to_spaces
(
name
)
def
get_description
(
self
,
html
=
False
):
"""
Return the resource or view docstring for use as this view's description.
Override to customize.
"""
description
=
None
# If this view has a resource that's been overridden,
# then try to use the resource's docstring
if
getattr
(
self
,
'resource'
,
None
)
not
in
_resource_classes
:
description
=
self
.
resource
.
__doc__
# Otherwise use the view docstring
if
not
description
:
description
=
self
.
__doc__
or
''
description
=
_remove_leading_indent
(
description
)
if
html
:
return
self
.
markup_description
(
description
)
return
description
def
markup_description
(
self
,
description
):
if
apply_markdown
:
description
=
apply_markdown
(
description
)
else
:
description
=
escape
(
description
)
.
replace
(
'
\n
'
,
'<br />'
)
return
mark_safe
(
description
)
def
http_method_not_allowed
(
self
,
request
,
*
args
,
**
kwargs
):
def
http_method_not_allowed
(
self
,
request
,
*
args
,
**
kwargs
):
"""
"""
Return an HTTP 405 error if an operation is called which does not have a handler method.
Return an HTTP 405 error if an operation is called which does not have a handler method.
...
@@ -164,8 +256,8 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
...
@@ -164,8 +256,8 @@ class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView):
def
options
(
self
,
request
,
*
args
,
**
kwargs
):
def
options
(
self
,
request
,
*
args
,
**
kwargs
):
response_obj
=
{
response_obj
=
{
'name'
:
get_name
(
self
),
'name'
:
self
.
get_name
(
),
'description'
:
get_description
(
self
),
'description'
:
self
.
get_description
(
),
'renders'
:
self
.
_rendered_media_types
,
'renders'
:
self
.
_rendered_media_types
,
'parses'
:
request
.
_parsed_media_types
,
'parses'
:
request
.
_parsed_media_types
,
}
}
...
...
docs/index.rst
View file @
279fa0d3
...
@@ -105,6 +105,8 @@ The following example exposes your `MyModel` model through an api. It will provi
...
@@ -105,6 +105,8 @@ The following example exposes your `MyModel` model through an api. It will provi
contents
contents
.. include:: ../CHANGELOG.rst
Indices and tables
Indices and tables
------------------
------------------
...
...
examples/permissionsexample/views.py
View file @
279fa0d3
...
@@ -2,14 +2,23 @@ from djangorestframework.views import View
...
@@ -2,14 +2,23 @@ from djangorestframework.views import View
from
djangorestframework.permissions
import
PerUserThrottling
,
IsAuthenticated
from
djangorestframework.permissions
import
PerUserThrottling
,
IsAuthenticated
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
class
PermissionsExampleView
(
View
):
class
PermissionsExampleView
(
View
):
"""
"""
A container view for permissions examples.
A container view for permissions examples.
"""
"""
def
get
(
self
,
request
):
def
get
(
self
,
request
):
return
[{
'name'
:
'Throttling Example'
,
'url'
:
reverse
(
'throttled-resource'
)},
return
[
{
'name'
:
'Logged in example'
,
'url'
:
reverse
(
'loggedin-resource'
)},]
{
'name'
:
'Throttling Example'
,
'url'
:
reverse
(
'throttled-resource'
)
},
{
'name'
:
'Logged in example'
,
'url'
:
reverse
(
'loggedin-resource'
)
},
]
class
ThrottlingExampleView
(
View
):
class
ThrottlingExampleView
(
View
):
...
@@ -20,7 +29,7 @@ class ThrottlingExampleView(View):
...
@@ -20,7 +29,7 @@ class ThrottlingExampleView(View):
throttle will be applied until 60 seconds have passed since the first request.
throttle will be applied until 60 seconds have passed since the first request.
"""
"""
permissions
=
(
PerUserThrottling
,
)
permissions
=
(
PerUserThrottling
,
)
throttle
=
'10/min'
throttle
=
'10/min'
def
get
(
self
,
request
):
def
get
(
self
,
request
):
...
@@ -29,13 +38,15 @@ class ThrottlingExampleView(View):
...
@@ -29,13 +38,15 @@ class ThrottlingExampleView(View):
"""
"""
return
"Successful response to GET request because throttle is not yet active."
return
"Successful response to GET request because throttle is not yet active."
class
LoggedInExampleView
(
View
):
class
LoggedInExampleView
(
View
):
"""
"""
You can login with **'test', 'test'.** or use curl:
You can login with **'test', 'test'.** or use curl:
`curl -X GET -H 'Accept: application/json' -u test:test http://localhost:8000/permissions-example`
`curl -X GET -H 'Accept: application/json' -u test:test http://localhost:8000/permissions-example`
"""
"""
permissions
=
(
IsAuthenticated
,
)
permissions
=
(
IsAuthenticated
,
)
def
get
(
self
,
request
):
def
get
(
self
,
request
):
return
'
Logged in or not?
'
return
'
You have permission to view this resource
'
examples/requirements-epio.txt
View file @
279fa0d3
Pygments==1.4
Pygments==1.4
Markdown==2.0.3
Markdown==2.0.3
djangorestframework
git+git://github.com/tomchristie/django-rest-framework.git
examples/settings.py
View file @
279fa0d3
...
@@ -53,16 +53,10 @@ MEDIA_ROOT = os.path.join(os.getenv('EPIO_DATA_DIRECTORY', '.'), 'media')
...
@@ -53,16 +53,10 @@ MEDIA_ROOT = os.path.join(os.getenv('EPIO_DATA_DIRECTORY', '.'), 'media')
# trailing slash if there is a path component (optional in other cases).
# trailing slash if there is a path component (optional in other cases).
# Examples: "http://media.lawrence.com", "http://example.com/media/"
# Examples: "http://media.lawrence.com", "http://example.com/media/"
# NOTE: None of the djangorestframework examples serve media content via MEDIA_URL.
# NOTE: None of the djangorestframework examples serve media content via MEDIA_URL.
MEDIA_URL
=
''
MEDIA_URL
=
'/uploads/'
STATIC_URL
=
'/static/'
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
# Examples: "http://foo.com/media/", "/media/".
# NOTE: djangorestframework does not require the admin app to be installed,
# but it does require the admin media be served. Django's test server will do
# this for you automatically, but in production you'll want to make sure you
# serve the admin media from somewhere.
ADMIN_MEDIA_PREFIX
=
'/static/admin'
# Make this unique, and don't share it with anybody.
# Make this unique, and don't share it with anybody.
SECRET_KEY
=
't&9mru2_k$t8e2-9uq-wu2a1)9v*us&j3i#lsqkt(lbx*vh1cu'
SECRET_KEY
=
't&9mru2_k$t8e2-9uq-wu2a1)9v*us&j3i#lsqkt(lbx*vh1cu'
...
@@ -102,6 +96,7 @@ INSTALLED_APPS = (
...
@@ -102,6 +96,7 @@ INSTALLED_APPS = (
'django.contrib.contenttypes'
,
'django.contrib.contenttypes'
,
'django.contrib.sessions'
,
'django.contrib.sessions'
,
'django.contrib.sites'
,
'django.contrib.sites'
,
'django.contrib.staticfiles'
,
'django.contrib.messages'
,
'django.contrib.messages'
,
'djangorestframework'
,
'djangorestframework'
,
...
...
examples/urls.py
View file @
279fa0d3
from
django.conf.urls.defaults
import
patterns
,
include
,
url
from
django.conf.urls.defaults
import
patterns
,
include
,
url
from
django.conf
import
settings
from
django.conf
import
settings
from
sandbox.views
import
Sandbox
from
sandbox.views
import
Sandbox
from
django.contrib.staticfiles.urls
import
staticfiles_urlpatterns
urlpatterns
=
patterns
(
''
,
urlpatterns
=
patterns
(
''
,
(
r'^$'
,
Sandbox
.
as_view
()),
(
r'^$'
,
Sandbox
.
as_view
()),
...
@@ -16,3 +17,4 @@ urlpatterns = patterns('',
...
@@ -16,3 +17,4 @@ urlpatterns = patterns('',
(
r'^'
,
include
(
'djangorestframework.urls'
)),
(
r'^'
,
include
(
'djangorestframework.urls'
)),
)
)
urlpatterns
+=
staticfiles_urlpatterns
()
tox.ini
View file @
279fa0d3
...
@@ -32,6 +32,7 @@ commands=
...
@@ -32,6 +32,7 @@ commands=
basepython
=
python2.5
basepython
=
python2.5
deps
=
deps
=
django
=
=1.2.4
django
=
=1.2.4
django-staticfiles>=1.1.2
coverage
=
=3.4
coverage
=
=3.4
URLObject>=0.6.0
URLObject>=0.6.0
unittest-xml-reporting
=
=1.2
unittest-xml-reporting
=
=1.2
...
@@ -43,6 +44,7 @@ deps=
...
@@ -43,6 +44,7 @@ deps=
basepython
=
python2.6
basepython
=
python2.6
deps
=
deps
=
django
=
=1.2.4
django
=
=1.2.4
django-staticfiles>=1.1.2
coverage
=
=3.4
coverage
=
=3.4
URLObject>=0.6.0
URLObject>=0.6.0
unittest-xml-reporting
=
=1.2
unittest-xml-reporting
=
=1.2
...
@@ -54,6 +56,7 @@ deps=
...
@@ -54,6 +56,7 @@ deps=
basepython
=
python2.7
basepython
=
python2.7
deps
=
deps
=
django
=
=1.2.4
django
=
=1.2.4
django-staticfiles>=1.1.2
coverage
=
=3.4
coverage
=
=3.4
URLObject>=0.6.0
URLObject>=0.6.0
unittest-xml-reporting
=
=1.2
unittest-xml-reporting
=
=1.2
...
@@ -135,6 +138,7 @@ commands=
...
@@ -135,6 +138,7 @@ commands=
python
examples/runtests.py
python
examples/runtests.py
deps
=
deps
=
django
=
=1.2.4
django
=
=1.2.4
django-staticfiles>=1.1.2
coverage
=
=3.4
coverage
=
=3.4
URLObject>=0.6.0
URLObject>=0.6.0
wsgiref
=
=0.1.2
wsgiref
=
=0.1.2
...
@@ -150,6 +154,7 @@ commands=
...
@@ -150,6 +154,7 @@ commands=
python
examples/runtests.py
python
examples/runtests.py
deps
=
deps
=
django
=
=1.2.4
django
=
=1.2.4
django-staticfiles>=1.1.2
coverage
=
=3.4
coverage
=
=3.4
URLObject>=0.6.0
URLObject>=0.6.0
wsgiref
=
=0.1.2
wsgiref
=
=0.1.2
...
@@ -165,6 +170,7 @@ commands=
...
@@ -165,6 +170,7 @@ commands=
python
examples/runtests.py
python
examples/runtests.py
deps
=
deps
=
django
=
=1.2.4
django
=
=1.2.4
django-staticfiles>=1.1.2
coverage
=
=3.4
coverage
=
=3.4
URLObject>=0.6.0
URLObject>=0.6.0
wsgiref
=
=0.1.2
wsgiref
=
=0.1.2
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment