Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support 1.8-alpha. #2446

Merged
merged 8 commits into from
Jan 23, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@ env:
matrix:
fast_finish: true
allow_failures:
- env: TOX_ENV=py34-django18alpha
- env: TOX_ENV=py33-django18alpha
- env: TOX_ENV=py32-django18alpha
- env: TOX_ENV=py27-django18alpha
- env: TOX_ENV=py34-djangomaster
- env: TOX_ENV=py33-djangomaster
- env: TOX_ENV=py32-djangomaster
Expand Down
1 change: 1 addition & 0 deletions env/pip-selfcheck.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"last_check":"2015-01-23T11:37:11Z","pypi_version":"6.0.6"}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be missing something, but why was this committed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how that happened. Now resolved - thanks!

2 changes: 1 addition & 1 deletion rest_framework/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ def get_filename(self, stream, media_type, parser_context):
if 'filename*' in filename_parm:
return self.get_encoded_filename(filename_parm)
return force_text(filename_parm['filename'])
except (AttributeError, KeyError):
except (AttributeError, KeyError, ValueError):
pass

def get_encoded_filename(self, filename_parm):
Expand Down
7 changes: 5 additions & 2 deletions rest_framework/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,13 @@ def status_text(self):

def __getstate__(self):
"""
Remove attributes from the response that shouldn't be cached
Remove attributes from the response that shouldn't be cached.
"""
state = super(Response, self).__getstate__()
for key in ('accepted_renderer', 'renderer_context', 'data'):
for key in (
'accepted_renderer', 'renderer_context', 'resolver_match',
'client', 'request', 'wsgi_request', '_closable_objects'
):
if key in state:
del state[key]
return state
10 changes: 8 additions & 2 deletions rest_framework/utils/model_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,22 +121,28 @@ def _get_reverse_relationships(opts):
"""
Returns an `OrderedDict` of field names to `RelationInfo`.
"""
# Note that we have a hack here to handle internal API differences for
# this internal API across Django 1.7 -> Django 1.8.
# See: https://code.djangoproject.com/ticket/24208

reverse_relations = OrderedDict()
for relation in opts.get_all_related_objects():
accessor_name = relation.get_accessor_name()
related = getattr(relation, 'related_model', relation.model)
reverse_relations[accessor_name] = RelationInfo(
model_field=None,
related=relation.model,
related=related,
to_many=relation.field.rel.multiple,
has_through_model=False
)

# Deal with reverse many-to-many relationships.
for relation in opts.get_all_related_many_to_many_objects():
accessor_name = relation.get_accessor_name()
related = getattr(relation, 'related_model', relation.model)
reverse_relations[accessor_name] = RelationInfo(
model_field=None,
related=relation.model,
related=related,
to_many=True,
has_through_model=(
(getattr(relation.field.rel, 'through', None) is not None)
Expand Down
1 change: 1 addition & 0 deletions tests/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,7 @@ def setUp(self):
for d in data:
DjangoFilterOrderingModel.objects.create(**d)

@unittest.skipUnless(django_filters, 'django-filter not installed')
def test_default_ordering(self):
class DjangoFilterOrderingView(generics.ListAPIView):
serializer_class = DjangoFilterOrderingSerializer
Expand Down
6 changes: 6 additions & 0 deletions tests/test_htmlrenderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,13 @@ def get_template(template_name, dirs=None):
return Template("example: {{ object }}")
raise TemplateDoesNotExist(template_name)

def select_template(template_name_list, dirs=None, using=None):
if template_name_list == ['example.html']:
return Template("example: {{ object }}")
raise TemplateDoesNotExist(template_name_list[0])

django.template.loader.get_template = get_template
django.template.loader.select_template = select_template

def tearDown(self):
"""
Expand Down
4 changes: 3 additions & 1 deletion tests/test_parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,9 @@ def test_get_encoded_filename(self):

self.__replace_content_disposition('inline; filename=fallback.txt; filename*=utf-8--ÀĥƦ.txt')
filename = parser.get_filename(self.stream, None, self.parser_context)
self.assertEqual(filename, 'fallback.txt')
# Malformed. Either None or 'fallback.txt' will be acceptable.
# See also https://code.djangoproject.com/ticket/24209
self.assertIn(filename, ('fallback.txt', None))

def __replace_content_disposition(self, disposition):
self.parser_context['request'].META['HTTP_CONTENT_DISPOSITION'] = disposition
85 changes: 12 additions & 73 deletions tests/test_renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
from collections import MutableMapping
import datetime
import json
import pickle
import re


Expand Down Expand Up @@ -618,84 +617,24 @@ class CacheRenderTest(TestCase):

urls = 'tests.test_renderers'

cache_key = 'just_a_cache_key'

@classmethod
def _get_pickling_errors(cls, obj, seen=None):
""" Return any errors that would be raised if `obj' is pickled
Courtesy of koffie @ http://stackoverflow.com/a/7218986/109897
"""
if seen is None:
seen = []
try:
state = obj.__getstate__()
except AttributeError:
return
if state is None:
return
if isinstance(state, tuple):
if not isinstance(state[0], dict):
state = state[1]
else:
state = state[0].update(state[1])
result = {}
for i in state:
try:
pickle.dumps(state[i], protocol=2)
except pickle.PicklingError:
if not state[i] in seen:
seen.append(state[i])
result[i] = cls._get_pickling_errors(state[i], seen)
return result

def http_resp(self, http_method, url):
"""
Simple wrapper for Client http requests
Removes the `client' and `request' attributes from as they are
added by django.test.client.Client and not part of caching
responses outside of tests.
"""
method = getattr(self.client, http_method)
resp = method(url)
resp._closable_objects = []
del resp.client, resp.request
try:
del resp.wsgi_request
except AttributeError:
pass
return resp

def test_obj_pickling(self):
"""
Test that responses are properly pickled
"""
resp = self.http_resp('get', '/cache')

# Make sure that no pickling errors occurred
self.assertEqual(self._get_pickling_errors(resp), {})

# Unfortunately LocMem backend doesn't raise PickleErrors but returns
# None instead.
cache.set(self.cache_key, resp)
self.assertTrue(cache.get(self.cache_key) is not None)

def test_head_caching(self):
"""
Test caching of HEAD requests
"""
resp = self.http_resp('head', '/cache')
cache.set(self.cache_key, resp)

cached_resp = cache.get(self.cache_key)
self.assertIsInstance(cached_resp, Response)
response = self.client.head('/cache')
cache.set('key', response)
cached_response = cache.get('key')
assert isinstance(cached_response, Response)
assert cached_response.content == response.content
assert cached_response.status_code == response.status_code

def test_get_caching(self):
"""
Test caching of GET requests
"""
resp = self.http_resp('get', '/cache')
cache.set(self.cache_key, resp)

cached_resp = cache.get(self.cache_key)
self.assertIsInstance(cached_resp, Response)
self.assertEqual(cached_resp.content, resp.content)
response = self.client.get('/cache')
cache.set('key', response)
cached_response = cache.get('key')
assert isinstance(cached_response, Response)
assert cached_response.content == response.content
assert cached_response.status_code == response.status_code
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ deps =
{py26,py27}-django{14,15}: django-oauth2-provider==0.2.3
{py26,py27}-django16: django-oauth2-provider==0.2.4
pytest-django==2.8.0
django-filter==0.7
{py26,py27,py32,py33,py34}-django{14,15,16,17}: django-filter==0.7
defusedxml==0.3
markdown>=2.1.0
PyYAML>=3.10
Expand Down