Commit 628c3568 by Anna

Merge remote-tracking branch 'upstream/master'

parents 60bb3b09 1d34bc0b
......@@ -105,7 +105,7 @@ Here's a brief example that demonstrates:
<html>
<head>
<script src="/static/rest_framework/js/coreapi-0.1.0.js"></script>
<script src="/docs/schema.js' %}"></script>
<script src="/docs/schema.js"></script>
<script>
const coreapi = window.coreapi
const schema = window.schema
......
......@@ -38,6 +38,32 @@ You can determine your currently installed version using `pip freeze`:
---
## 3.6.x series
### 3.6.2
**Date**: [10th March 2017][3.6.2-milestone]
* Support for Safari & IE in API docs. ([#4959][gh4959], [#4961][gh4961])
* Add missing `mark_safe` in API docs template tags. ([#4952][gh4952], [#4953][gh4953])
* Add missing glyicon fonts. ([#4950][gh4950], [#4951][gh4951])
* Fix One-to-one fields in API docs. ([#4955][gh4955], [#4956][gh4956])
* Test clean ups. ([#4949][gh4949])
### 3.6.1
**Date**: [9th March 2017][3.6.1-milestone]
* Ensure `markdown` dependancy is optional. ([#4947][gh4947])
### 3.6.0
**Date**: [9th March 2017][3.6.0-milestone]
See the [release announcement][3.6-release].
---
## 3.5.x series
### 3.5.4
......@@ -625,6 +651,7 @@ For older release notes, [please see the version 2.x documentation][old-release-
[ticket-582]: https://github.com/tomchristie/django-rest-framework/issues/582
[rfc-6266]: http://tools.ietf.org/html/rfc6266#section-4.3
[old-release-notes]: https://github.com/tomchristie/django-rest-framework/blob/version-2.4.x/docs/topics/release-notes.md
[3.6-release]: 3.6-announcement.md
[3.0.1-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.1+Release%22
[3.0.2-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.2+Release%22
......@@ -658,6 +685,9 @@ For older release notes, [please see the version 2.x documentation][old-release-
[3.5.2-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.5.2+Release%22
[3.5.3-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.5.3+Release%22
[3.5.4-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.5.4+Release%22
[3.6.0-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.6.0+Release%22
[3.6.1-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.6.1+Release%22
[3.6.2-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.6.2+Release%22
<!-- 3.0.1 -->
[gh2013]: https://github.com/tomchristie/django-rest-framework/issues/2013
......@@ -1254,3 +1284,17 @@ For older release notes, [please see the version 2.x documentation][old-release-
[gh4634]: https://github.com/tomchristie/django-rest-framework/issues/4634
[gh4669]: https://github.com/tomchristie/django-rest-framework/issues/4669
[gh4712]: https://github.com/tomchristie/django-rest-framework/issues/4712
<!-- 3.6.1 -->
[gh4947]: https://github.com/tomchristie/django-rest-framework/issues/4947
<!-- 3.6.2 -->
[gh4959]: https://github.com/tomchristie/django-rest-framework/issues/4959
[gh4961]: https://github.com/tomchristie/django-rest-framework/issues/4961
[gh4952]: https://github.com/tomchristie/django-rest-framework/issues/4952
[gh4953]: https://github.com/tomchristie/django-rest-framework/issues/4953
[gh4950]: https://github.com/tomchristie/django-rest-framework/issues/4950
[gh4951]: https://github.com/tomchristie/django-rest-framework/issues/4951
[gh4955]: https://github.com/tomchristie/django-rest-framework/issues/4955
[gh4956]: https://github.com/tomchristie/django-rest-framework/issues/4956
[gh4949]: https://github.com/tomchristie/django-rest-framework/issues/4949
......@@ -8,7 +8,7 @@ ______ _____ _____ _____ __
"""
__title__ = 'Django REST framework'
__version__ = '3.6.1'
__version__ = '3.6.2'
__author__ = 'Tom Christie'
__license__ = 'BSD 2-Clause'
__copyright__ = 'Copyright 2011-2017 Tom Christie'
......
......@@ -531,7 +531,7 @@ class SchemaGenerator(object):
try:
model_field = model._meta.get_field(variable)
except:
pass
model_field = None
if model_field is not None and model_field.verbose_name:
title = force_text(model_field.verbose_name)
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -7,7 +7,7 @@ function normalizeHTTPHeader (str) {
.replace(/(Md5)/g, function ($1) { return 'MD5' })
}
let responseDisplay = 'data'
var responseDisplay = 'data'
const coreapi = window.coreapi
const schema = window.schema
......@@ -28,6 +28,39 @@ $('#language-control li').click(function (event) {
codeBlocks.filter('[data-language="' + language +'"]').removeClass("hide")
})
function formEntries (form) {
// Polyfill for new FormData(form).entries()
var formData = new FormData(form)
if (formData.entries !== undefined) {
return formData.entries()
}
var entries = []
for (var {name, type, value, files, checked, selectedOptions} of Array.from(form.elements)) {
if (!name) {
continue
}
if (type === 'file') {
for (var file of files) {
entries.push([name, file])
}
} else if (type === 'select-multiple' || type === 'select-one') {
for (var elm of Array.from(selectedOptions)) {
entries.push([name, elm.value])
}
} else if (type === 'checkbox') {
if (checked) {
entries.push([name, value])
}
} else {
entries.push([name, value])
}
}
return entries
}
// API Explorer
$('form.api-interaction').submit(function(event) {
event.preventDefault();
......@@ -36,23 +69,23 @@ $('form.api-interaction').submit(function(event) {
const key = form.data("key");
var params = {};
const formData = new FormData(form.get()[0]);
for (var [paramKey, paramValue] of formData.entries()) {
const entries = formEntries(form.get()[0]);
for (var [paramKey, paramValue] of entries) {
var elem = form.find("[name=" + paramKey + "]")
var dataType = elem.data('type') || 'string'
if (dataType === 'integer' && paramValue) {
let value = parseInt(paramValue)
var value = parseInt(paramValue)
if (!isNaN(value)) {
params[paramKey] = value
}
} else if (dataType === 'number' && paramValue) {
let value = parseFloat(paramValue)
var value = parseFloat(paramValue)
if (!isNaN(value)) {
params[paramKey] = value
}
} else if (dataType === 'boolean' && paramValue) {
let value = {
var value = {
'true': true,
'false': false
}[paramValue.toLowerCase()]
......@@ -86,7 +119,7 @@ $('form.api-interaction').submit(function(event) {
function requestCallback(request) {
// Fill in the "GET /foo/" display.
let parser = document.createElement('a');
var parser = document.createElement('a');
parser.href = request.url;
const method = request.options.method
const path = parser.pathname + parser.hash + parser.search
......@@ -111,7 +144,7 @@ $('form.api-interaction').submit(function(event) {
// Fill in the Raw HTTP response display.
var panelText = 'HTTP/1.1 ' + response.status + ' ' + response.statusText + '\n';
response.headers.forEach((header, key) => {
response.headers.forEach(function(header, key) {
panelText += normalizeHTTPHeader(key) + ': ' + header + '\n'
})
if (responseText) {
......@@ -121,7 +154,7 @@ $('form.api-interaction').submit(function(event) {
}
// Instantiate a client to make the outgoing request.
let options = {
var options = {
requestCallback: requestCallback,
responseCallback: responseCallback,
}
......
let codec = new window.coreapi.codecs.CoreJSONCodec()
let coreJSON = window.atob('{{ schema }}')
var codec = new window.coreapi.codecs.CoreJSONCodec()
var coreJSON = window.atob('{{ schema }}')
window.schema = codec.decode(coreJSON)
......@@ -63,14 +63,14 @@ def form_for_link(link):
if field.required
]
schema = coreschema.Object(properties=properties, required=required)
return coreschema.render_to_form(schema)
return mark_safe(coreschema.render_to_form(schema))
@register.simple_tag
def render_markdown(markdown_text):
if not markdown:
return markdown_text
return markdown.markdown(markdown_text)
return mark_safe(markdown.markdown(markdown_text))
@register.simple_tag
......
......@@ -69,7 +69,7 @@ class TestAPITestClient(TestCase):
self.client.credentials(HTTP_AUTHORIZATION='example')
for _ in range(0, 3):
response = self.client.get('/view/')
self.assertEqual(response.data['auth'], 'example')
assert response.data['auth'] == 'example'
def test_force_authenticate(self):
"""
......@@ -78,7 +78,7 @@ class TestAPITestClient(TestCase):
user = User.objects.create_user('example', 'example@example.com')
self.client.force_authenticate(user)
response = self.client.get('/view/')
self.assertEqual(response.data['user'], 'example')
assert response.data['user'] == 'example'
def test_force_authenticate_with_sessions(self):
"""
......@@ -89,16 +89,16 @@ class TestAPITestClient(TestCase):
# First request does not yet have an active session
response = self.client.get('/session-view/')
self.assertEqual(response.data['active_session'], False)
assert response.data['active_session'] is False
# Subsequent requests have an active session
response = self.client.get('/session-view/')
self.assertEqual(response.data['active_session'], True)
assert response.data['active_session'] is True
# Force authenticating as `None` should also logout the user session.
self.client.force_authenticate(None)
response = self.client.get('/session-view/')
self.assertEqual(response.data['active_session'], False)
assert response.data['active_session'] is False
def test_csrf_exempt_by_default(self):
"""
......@@ -107,7 +107,7 @@ class TestAPITestClient(TestCase):
User.objects.create_user('example', 'example@example.com', 'password')
self.client.login(username='example', password='password')
response = self.client.post('/view/')
self.assertEqual(response.status_code, 200)
assert response.status_code == 200
def test_explicitly_enforce_csrf_checks(self):
"""
......@@ -118,8 +118,8 @@ class TestAPITestClient(TestCase):
client.login(username='example', password='password')
response = client.post('/view/')
expected = {'detail': 'CSRF Failed: CSRF cookie not set.'}
self.assertEqual(response.status_code, 403)
self.assertEqual(response.data, expected)
assert response.status_code == 403
assert response.data == expected
def test_can_logout(self):
"""
......@@ -127,10 +127,10 @@ class TestAPITestClient(TestCase):
"""
self.client.credentials(HTTP_AUTHORIZATION='example')
response = self.client.get('/view/')
self.assertEqual(response.data['auth'], 'example')
assert response.data['auth'] == 'example'
self.client.logout()
response = self.client.get('/view/')
self.assertEqual(response.data['auth'], b'')
assert response.data['auth'] == b''
def test_logout_resets_force_authenticate(self):
"""
......@@ -139,50 +139,50 @@ class TestAPITestClient(TestCase):
user = User.objects.create_user('example', 'example@example.com', 'password')
self.client.force_authenticate(user)
response = self.client.get('/view/')
self.assertEqual(response.data['user'], 'example')
assert response.data['user'] == 'example'
self.client.logout()
response = self.client.get('/view/')
self.assertEqual(response.data['user'], '')
assert response.data['user'] == ''
def test_follow_redirect(self):
"""
Follow redirect by setting follow argument.
"""
response = self.client.get('/redirect-view/')
self.assertEqual(response.status_code, 302)
assert response.status_code == 302
response = self.client.get('/redirect-view/', follow=True)
self.assertIsNotNone(response.redirect_chain)
self.assertEqual(response.status_code, 200)
assert response.redirect_chain is not None
assert response.status_code == 200
response = self.client.post('/redirect-view/')
self.assertEqual(response.status_code, 302)
assert response.status_code == 302
response = self.client.post('/redirect-view/', follow=True)
self.assertIsNotNone(response.redirect_chain)
self.assertEqual(response.status_code, 200)
assert response.redirect_chain is not None
assert response.status_code == 200
response = self.client.put('/redirect-view/')
self.assertEqual(response.status_code, 302)
assert response.status_code == 302
response = self.client.put('/redirect-view/', follow=True)
self.assertIsNotNone(response.redirect_chain)
self.assertEqual(response.status_code, 200)
assert response.redirect_chain is not None
assert response.status_code == 200
response = self.client.patch('/redirect-view/')
self.assertEqual(response.status_code, 302)
assert response.status_code == 302
response = self.client.patch('/redirect-view/', follow=True)
self.assertIsNotNone(response.redirect_chain)
self.assertEqual(response.status_code, 200)
assert response.redirect_chain is not None
assert response.status_code == 200
response = self.client.delete('/redirect-view/')
self.assertEqual(response.status_code, 302)
assert response.status_code == 302
response = self.client.delete('/redirect-view/', follow=True)
self.assertIsNotNone(response.redirect_chain)
self.assertEqual(response.status_code, 200)
assert response.redirect_chain is not None
assert response.status_code == 200
response = self.client.options('/redirect-view/')
self.assertEqual(response.status_code, 302)
assert response.status_code == 302
response = self.client.options('/redirect-view/', follow=True)
self.assertIsNotNone(response.redirect_chain)
self.assertEqual(response.status_code, 200)
assert response.redirect_chain is not None
assert response.status_code == 200
def test_invalid_multipart_data(self):
"""
......@@ -200,8 +200,8 @@ class TestAPITestClient(TestCase):
data=None,
content_type='application/json'
)
self.assertEqual(response.status_code, 200, response.content)
self.assertEqual(response.data, {"flag": True})
assert response.status_code == 200
assert response.data == {"flag": True}
class TestAPIRequestFactory(TestCase):
......@@ -214,7 +214,7 @@ class TestAPIRequestFactory(TestCase):
request = factory.post('/view/')
request.user = user
response = view(request)
self.assertEqual(response.status_code, 200)
assert response.status_code == 200
def test_explicitly_enforce_csrf_checks(self):
"""
......@@ -226,8 +226,8 @@ class TestAPIRequestFactory(TestCase):
request.user = user
response = view(request)
expected = {'detail': 'CSRF Failed: CSRF cookie not set.'}
self.assertEqual(response.status_code, 403)
self.assertEqual(response.data, expected)
assert response.status_code == 403
assert response.data == expected
def test_invalid_format(self):
"""
......@@ -249,7 +249,7 @@ class TestAPIRequestFactory(TestCase):
request = factory.get('/view')
force_authenticate(request, user=user)
response = view(request)
self.assertEqual(response.data['user'], 'example')
assert response.data['user'] == 'example'
def test_upload_file(self):
# This is a 1x1 black png
......@@ -264,13 +264,13 @@ class TestAPIRequestFactory(TestCase):
"""
factory = APIRequestFactory()
request = factory.get('/view/?demo=test')
self.assertEqual(dict(request.GET), {'demo': ['test']})
assert dict(request.GET) == {'demo': ['test']}
request = factory.get('/view/', {'demo': 'test'})
self.assertEqual(dict(request.GET), {'demo': ['test']})
assert dict(request.GET) == {'demo': ['test']}
def test_request_factory_url_arguments_with_unicode(self):
factory = APIRequestFactory()
request = factory.get('/view/?demo=testé')
self.assertEqual(dict(request.GET), {'demo': ['testé']})
assert dict(request.GET) == {'demo': ['testé']}
request = factory.get('/view/', {'demo': 'testé'})
self.assertEqual(dict(request.GET), {'demo': ['testé']})
assert dict(request.GET) == {'demo': ['testé']}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment