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
dee3f78c
Commit
dee3f78c
authored
Sep 26, 2014
by
Tom Christie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
FileField and ImageField
parent
ac71d9aa
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
156 additions
and
48 deletions
+156
-48
requirements-test.txt
+0
-1
rest_framework/compat.py
+0
-9
rest_framework/fields.py
+64
-18
rest_framework/settings.py
+2
-1
tests/test_fields.py
+90
-3
tox.ini
+0
-16
No files found.
requirements-test.txt
View file @
dee3f78c
...
...
@@ -13,4 +13,3 @@ django-filter>=0.5.4
django-oauth-plus>=2.2.1
oauth2>=1.5.211
django-oauth2-provider>=0.2.4
Pillow==2.3.0
rest_framework/compat.py
View file @
dee3f78c
...
...
@@ -84,15 +84,6 @@ except ImportError:
from
collections
import
UserDict
from
collections
import
MutableMapping
as
DictMixin
# Try to import PIL in either of the two ways it can end up installed.
try
:
from
PIL
import
Image
except
ImportError
:
try
:
import
Image
except
ImportError
:
Image
=
None
def
get_model_name
(
model_cls
):
try
:
...
...
rest_framework/fields.py
View file @
dee3f78c
from
django
import
forms
from
django.conf
import
settings
from
django.core
import
validators
from
django.core.exceptions
import
ValidationError
...
...
@@ -427,8 +428,6 @@ class CharField(Field):
return
str
(
data
)
def
to_representation
(
self
,
value
):
if
value
is
None
:
return
None
return
str
(
value
)
...
...
@@ -446,8 +445,6 @@ class EmailField(CharField):
return
str
(
data
)
.
strip
()
def
to_representation
(
self
,
value
):
if
value
is
None
:
return
None
return
str
(
value
)
.
strip
()
...
...
@@ -513,8 +510,6 @@ class IntegerField(Field):
return
data
def
to_representation
(
self
,
value
):
if
value
is
None
:
return
None
return
int
(
value
)
...
...
@@ -543,8 +538,6 @@ class FloatField(Field):
self
.
fail
(
'invalid'
)
def
to_representation
(
self
,
value
):
if
value
is
None
:
return
None
return
float
(
value
)
...
...
@@ -616,9 +609,6 @@ class DecimalField(Field):
return
value
def
to_representation
(
self
,
value
):
if
value
in
(
None
,
''
):
return
None
if
not
isinstance
(
value
,
decimal
.
Decimal
):
value
=
decimal
.
Decimal
(
str
(
value
)
.
strip
())
...
...
@@ -689,7 +679,7 @@ class DateTimeField(Field):
self
.
fail
(
'invalid'
,
format
=
humanized_format
)
def
to_representation
(
self
,
value
):
if
value
is
None
or
self
.
format
is
None
:
if
self
.
format
is
None
:
return
value
if
self
.
format
.
lower
()
==
ISO_8601
:
...
...
@@ -741,7 +731,7 @@ class DateField(Field):
self
.
fail
(
'invalid'
,
format
=
humanized_format
)
def
to_representation
(
self
,
value
):
if
value
is
None
or
self
.
format
is
None
:
if
self
.
format
is
None
:
return
value
# Applying a `DateField` to a datetime value is almost always
...
...
@@ -795,7 +785,7 @@ class TimeField(Field):
self
.
fail
(
'invalid'
,
format
=
humanized_format
)
def
to_representation
(
self
,
value
):
if
value
is
None
or
self
.
format
is
None
:
if
self
.
format
is
None
:
return
value
# Applying a `TimeField` to a datetime value is almost always
...
...
@@ -875,14 +865,68 @@ class MultipleChoiceField(ChoiceField):
# File types...
class
FileField
(
Field
):
pass
# TODO
default_error_messages
=
{
'required'
:
_
(
"No file was submitted."
),
'invalid'
:
_
(
"The submitted data was not a file. Check the encoding type on the form."
),
'no_name'
:
_
(
"No filename could be determined."
),
'empty'
:
_
(
"The submitted file is empty."
),
'max_length'
:
_
(
'Ensure this filename has at most {max_length} characters (it has {length}).'
),
}
use_url
=
api_settings
.
UPLOADED_FILES_USE_URL
def
__init__
(
self
,
*
args
,
**
kwargs
):
self
.
max_length
=
kwargs
.
pop
(
'max_length'
,
None
)
self
.
allow_empty_file
=
kwargs
.
pop
(
'allow_empty_file'
,
False
)
self
.
use_url
=
kwargs
.
pop
(
'use_url'
,
self
.
use_url
)
super
(
FileField
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
class
ImageField
(
Field
):
pass
# TODO
def
to_internal_value
(
self
,
data
):
try
:
# `UploadedFile` objects should have name and size attributes.
file_name
=
data
.
name
file_size
=
data
.
size
except
AttributeError
:
self
.
fail
(
'invalid'
)
if
not
file_name
:
self
.
fail
(
'no_name'
)
if
not
self
.
allow_empty_file
and
not
file_size
:
self
.
fail
(
'empty'
)
if
self
.
max_length
and
len
(
file_name
)
>
self
.
max_length
:
self
.
fail
(
'max_length'
,
max_length
=
self
.
max_length
,
length
=
len
(
file_name
))
# Advanced field types...
return
data
def
to_representation
(
self
,
value
):
if
self
.
use_url
:
return
settings
.
MEDIA_URL
+
value
.
url
return
value
.
name
class
ImageField
(
FileField
):
default_error_messages
=
{
'invalid_image'
:
_
(
'Upload a valid image. The file you uploaded was either not an '
'image or a corrupted image.'
),
}
def
__init__
(
self
,
*
args
,
**
kwargs
):
self
.
_DjangoImageField
=
kwargs
.
pop
(
'_DjangoImageField'
,
forms
.
ImageField
)
super
(
ImageField
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
def
to_internal_value
(
self
,
data
):
# Image validation is a bit grungy, so we'll just outright
# defer to Django's implementation so we don't need to
# consider it, or treat PIL as a test dependancy.
file_object
=
super
(
ImageField
,
self
)
.
to_internal_value
(
data
)
django_field
=
self
.
_DjangoImageField
()
django_field
.
error_messages
=
self
.
error_messages
django_field
.
to_python
(
file_object
)
return
file_object
# Composite field types...
class
ListField
(
Field
):
child
=
None
...
...
@@ -922,6 +966,8 @@ class ListField(Field):
return
[
self
.
child
.
to_representation
(
item
)
for
item
in
data
]
# Miscellaneous field types...
class
ReadOnlyField
(
Field
):
"""
A read-only field that simply returns the field value.
...
...
rest_framework/settings.py
View file @
dee3f78c
...
...
@@ -110,7 +110,8 @@ DEFAULTS = {
# Encoding
'UNICODE_JSON'
:
True
,
'COMPACT_JSON'
:
True
,
'COERCE_DECIMAL_TO_STRING'
:
True
'COERCE_DECIMAL_TO_STRING'
:
True
,
'UPLOADED_FILES_USE_URL'
:
True
}
...
...
tests/test_fields.py
View file @
dee3f78c
from
decimal
import
Decimal
from
django.core.exceptions
import
ValidationError
from
django.utils
import
timezone
from
rest_framework
import
fields
,
serializers
import
datetime
...
...
@@ -516,7 +517,7 @@ class TestDecimalField(FieldValues):
Decimal
(
'1.0'
):
'1.0'
,
Decimal
(
'0.0'
):
'0.0'
,
Decimal
(
'1.09'
):
'1.1'
,
Decimal
(
'0.04'
):
'0.0'
,
Decimal
(
'0.04'
):
'0.0'
}
field
=
fields
.
DecimalField
(
max_digits
=
3
,
decimal_places
=
1
)
...
...
@@ -576,7 +577,7 @@ class TestDateField(FieldValues):
datetime
.
datetime
(
2001
,
1
,
1
,
12
,
00
):
[
'Expected a date but got a datetime.'
],
}
outputs
=
{
datetime
.
date
(
2001
,
1
,
1
):
'2001-01-01'
,
datetime
.
date
(
2001
,
1
,
1
):
'2001-01-01'
}
field
=
fields
.
DateField
()
...
...
@@ -639,7 +640,7 @@ class TestDateTimeField(FieldValues):
}
outputs
=
{
datetime
.
datetime
(
2001
,
1
,
1
,
13
,
00
):
'2001-01-01T13:00:00'
,
datetime
.
datetime
(
2001
,
1
,
1
,
13
,
00
,
tzinfo
=
timezone
.
UTC
()):
'2001-01-01T13:00:00Z'
,
datetime
.
datetime
(
2001
,
1
,
1
,
13
,
00
,
tzinfo
=
timezone
.
UTC
()):
'2001-01-01T13:00:00Z'
}
field
=
fields
.
DateTimeField
(
default_timezone
=
timezone
.
UTC
())
...
...
@@ -847,6 +848,92 @@ class TestMultipleChoiceField(FieldValues):
)
# File fields...
class
MockFile
:
def
__init__
(
self
,
name
=
''
,
size
=
0
,
url
=
''
):
self
.
name
=
name
self
.
size
=
size
self
.
url
=
url
def
__eq__
(
self
,
other
):
return
(
isinstance
(
other
,
MockFile
)
and
self
.
name
==
other
.
name
and
self
.
size
==
other
.
size
and
self
.
url
==
other
.
url
)
class
TestFileField
(
FieldValues
):
"""
Values for `FileField`.
"""
valid_inputs
=
[
(
MockFile
(
name
=
'example'
,
size
=
10
),
MockFile
(
name
=
'example'
,
size
=
10
))
]
invalid_inputs
=
[
(
'invalid'
,
[
'The submitted data was not a file. Check the encoding type on the form.'
]),
(
MockFile
(
name
=
'example.txt'
,
size
=
0
),
[
'The submitted file is empty.'
]),
(
MockFile
(
name
=
''
,
size
=
10
),
[
'No filename could be determined.'
]),
(
MockFile
(
name
=
'x'
*
100
,
size
=
10
),
[
'Ensure this filename has at most 10 characters (it has 100).'
])
]
outputs
=
[
(
MockFile
(
name
=
'example.txt'
,
url
=
'/example.txt'
),
'/example.txt'
)
]
field
=
fields
.
FileField
(
max_length
=
10
)
class
TestFieldFieldWithName
(
FieldValues
):
"""
Values for `FileField` with a filename output instead of URLs.
"""
valid_inputs
=
{}
invalid_inputs
=
{}
outputs
=
[
(
MockFile
(
name
=
'example.txt'
,
url
=
'/example.txt'
),
'example.txt'
)
]
field
=
fields
.
FileField
(
use_url
=
False
)
# Stub out mock Django `forms.ImageField` class so we don't *actually*
# call into it's regular validation, or require PIL for testing.
class
FailImageValidation
(
object
):
def
to_python
(
self
,
value
):
raise
ValidationError
(
self
.
error_messages
[
'invalid_image'
])
class
PassImageValidation
(
object
):
def
to_python
(
self
,
value
):
return
value
class
TestInvalidImageField
(
FieldValues
):
"""
Values for an invalid `ImageField`.
"""
valid_inputs
=
{}
invalid_inputs
=
[
(
MockFile
(
name
=
'example.txt'
,
size
=
10
),
[
'Upload a valid image. The file you uploaded was either not an image or a corrupted image.'
])
]
outputs
=
{}
field
=
fields
.
ImageField
(
_DjangoImageField
=
FailImageValidation
)
class
TestValidImageField
(
FieldValues
):
"""
Values for an valid `ImageField`.
"""
valid_inputs
=
[
(
MockFile
(
name
=
'example.txt'
,
size
=
10
),
MockFile
(
name
=
'example.txt'
,
size
=
10
))
]
invalid_inputs
=
{}
outputs
=
{}
field
=
fields
.
ImageField
(
_DjangoImageField
=
PassImageValidation
)
# Composite fields...
class
TestListField
(
FieldValues
):
"""
Values for `ListField`.
...
...
tox.ini
View file @
dee3f78c
...
...
@@ -21,7 +21,6 @@ basepython = python3.4
deps
=
Django==1.7
django-filter
=
=0.7
defusedxml
=
=0.3
Pillow
=
=2.3.0
pytest-django
=
=2.6.1
[testenv:py3.3-django1.7]
...
...
@@ -29,7 +28,6 @@ basepython = python3.3
deps
=
Django==1.7
django-filter
=
=0.7
defusedxml
=
=0.3
Pillow
=
=2.3.0
pytest-django
=
=2.6.1
[testenv:py3.2-django1.7]
...
...
@@ -37,7 +35,6 @@ basepython = python3.2
deps
=
Django==1.7
django-filter
=
=0.7
defusedxml
=
=0.3
Pillow
=
=2.3.0
pytest-django
=
=2.6.1
[testenv:py2.7-django1.7]
...
...
@@ -49,7 +46,6 @@ deps = Django==1.7
# oauth2==1.5.211
# django-oauth2-provider==0.2.4
django-guardian
=
=1.2.3
Pillow
=
=2.3.0
pytest-django
=
=2.6.1
[testenv:py3.4-django1.6]
...
...
@@ -57,7 +53,6 @@ basepython = python3.4
deps
=
Django==1.6.3
django-filter
=
=0.7
defusedxml
=
=0.3
Pillow
=
=2.3.0
pytest-django
=
=2.6.1
[testenv:py3.3-django1.6]
...
...
@@ -65,7 +60,6 @@ basepython = python3.3
deps
=
Django==1.6.3
django-filter
=
=0.7
defusedxml
=
=0.3
Pillow
=
=2.3.0
pytest-django
=
=2.6.1
[testenv:py3.2-django1.6]
...
...
@@ -73,7 +67,6 @@ basepython = python3.2
deps
=
Django==1.6.3
django-filter
=
=0.7
defusedxml
=
=0.3
Pillow
=
=2.3.0
pytest-django
=
=2.6.1
[testenv:py2.7-django1.6]
...
...
@@ -85,7 +78,6 @@ deps = Django==1.6.3
oauth2
=
=1.5.211
django-oauth2-provider
=
=0.2.4
django-guardian
=
=1.2.3
Pillow
=
=2.3.0
pytest-django
=
=2.6.1
[testenv:py2.6-django1.6]
...
...
@@ -97,7 +89,6 @@ deps = Django==1.6.3
oauth2
=
=1.5.211
django-oauth2-provider
=
=0.2.4
django-guardian
=
=1.2.3
Pillow
=
=2.3.0
pytest-django
=
=2.6.1
[testenv:py3.4-django1.5]
...
...
@@ -105,7 +96,6 @@ basepython = python3.4
deps
=
django==1.5.6
django-filter
=
=0.7
defusedxml
=
=0.3
Pillow
=
=2.3.0
pytest-django
=
=2.6.1
[testenv:py3.3-django1.5]
...
...
@@ -113,7 +103,6 @@ basepython = python3.3
deps
=
django==1.5.6
django-filter
=
=0.7
defusedxml
=
=0.3
Pillow
=
=2.3.0
pytest-django
=
=2.6.1
[testenv:py3.2-django1.5]
...
...
@@ -121,7 +110,6 @@ basepython = python3.2
deps
=
django==1.5.6
django-filter
=
=0.7
defusedxml
=
=0.3
Pillow
=
=2.3.0
pytest-django
=
=2.6.1
[testenv:py2.7-django1.5]
...
...
@@ -133,7 +121,6 @@ deps = django==1.5.6
oauth2
=
=1.5.211
django-oauth2-provider
=
=0.2.3
django-guardian
=
=1.2.3
Pillow
=
=2.3.0
pytest-django
=
=2.6.1
[testenv:py2.6-django1.5]
...
...
@@ -145,7 +132,6 @@ deps = django==1.5.6
oauth2
=
=1.5.211
django-oauth2-provider
=
=0.2.3
django-guardian
=
=1.2.3
Pillow
=
=2.3.0
pytest-django
=
=2.6.1
[testenv:py2.7-django1.4]
...
...
@@ -157,7 +143,6 @@ deps = django==1.4.11
oauth2
=
=1.5.211
django-oauth2-provider
=
=0.2.3
django-guardian
=
=1.2.3
Pillow
=
=2.3.0
pytest-django
=
=2.6.1
[testenv:py2.6-django1.4]
...
...
@@ -169,5 +154,4 @@ deps = django==1.4.11
oauth2
=
=1.5.211
django-oauth2-provider
=
=0.2.3
django-guardian
=
=1.2.3
Pillow
=
=2.3.0
pytest-django
=
=2.6.1
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