mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-06-20 21:43:22 +03:00
Login/Logout and FlyWheel API link in HTML emitter
This commit is contained in:
parent
e227c38b33
commit
216baa551f
|
@ -1,4 +1,7 @@
|
||||||
# Django settings for src project.
|
# Django settings for src project.
|
||||||
|
import os
|
||||||
|
|
||||||
|
BASE_DIR = os.path.dirname(__file__)
|
||||||
|
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
TEMPLATE_DEBUG = DEBUG
|
TEMPLATE_DEBUG = DEBUG
|
||||||
|
@ -81,6 +84,7 @@ TEMPLATE_DIRS = (
|
||||||
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
|
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
|
||||||
# Always use forward slashes, even on Windows.
|
# Always use forward slashes, even on Windows.
|
||||||
# Don't forget to use absolute paths, not relative paths.
|
# Don't forget to use absolute paths, not relative paths.
|
||||||
|
os.path.join(BASE_DIR, 'templates')
|
||||||
)
|
)
|
||||||
|
|
||||||
INSTALLED_APPS = (
|
INSTALLED_APPS = (
|
||||||
|
|
|
@ -7,6 +7,8 @@ urlpatterns = patterns('',
|
||||||
(r'pygments-example/', include('pygments_api.urls')),
|
(r'pygments-example/', include('pygments_api.urls')),
|
||||||
(r'^blog-post-example/', include('blogpost.urls')),
|
(r'^blog-post-example/', include('blogpost.urls')),
|
||||||
(r'^object-store-example/', include('objectstore.urls')),
|
(r'^object-store-example/', include('objectstore.urls')),
|
||||||
|
(r'^accounts/login/$', 'django.contrib.auth.views.login'),
|
||||||
|
(r'^accounts/logout/$', 'django.contrib.auth.views.logout'),
|
||||||
(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||||
(r'^admin/', include(admin.site.urls)),
|
(r'^admin/', include(admin.site.urls)),
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
from django.conf import settings
|
||||||
from django.template import RequestContext, loader
|
from django.template import RequestContext, loader
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from flywheel.response import NoContent
|
from flywheel.response import NoContent
|
||||||
|
|
||||||
from utils import dict2xml
|
from utils import dict2xml, url_resolves
|
||||||
import string
|
import string
|
||||||
try:
|
try:
|
||||||
import json
|
import json
|
||||||
|
@ -12,6 +13,7 @@ except ImportError:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BaseEmitter(object):
|
class BaseEmitter(object):
|
||||||
media_type = None
|
media_type = None
|
||||||
|
|
||||||
|
@ -118,13 +120,22 @@ class DocumentingTemplateEmitter(BaseEmitter):
|
||||||
content = self._get_content(self.resource, output)
|
content = self._get_content(self.resource, output)
|
||||||
form_instance = self._get_form_instance(self.resource)
|
form_instance = self._get_form_instance(self.resource)
|
||||||
|
|
||||||
|
if url_resolves(settings.LOGIN_URL) and url_resolves(settings.LOGOUT_URL):
|
||||||
|
login_url = "%s?next=%s" % (settings.LOGIN_URL, self.resource.request.path)
|
||||||
|
logout_url = "%s?next=%s" % (settings.LOGOUT_URL, self.resource.request.path)
|
||||||
|
else:
|
||||||
|
login_url = None
|
||||||
|
logout_url = None
|
||||||
|
|
||||||
template = loader.get_template(self.template)
|
template = loader.get_template(self.template)
|
||||||
context = RequestContext(self.resource.request, {
|
context = RequestContext(self.resource.request, {
|
||||||
'content': content,
|
'content': content,
|
||||||
'resource': self.resource,
|
'resource': self.resource,
|
||||||
'request': self.resource.request,
|
'request': self.resource.request,
|
||||||
'response': self.resource.response,
|
'response': self.resource.response,
|
||||||
'form': form_instance
|
'form': form_instance,
|
||||||
|
'login_url': login_url,
|
||||||
|
'logout_url': logout_url,
|
||||||
})
|
})
|
||||||
|
|
||||||
ret = template.render(context)
|
ret = template.render(context)
|
||||||
|
|
|
@ -7,9 +7,7 @@ from flywheel.response import status, Response, ResponseException
|
||||||
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import re
|
import re
|
||||||
from itertools import chain
|
|
||||||
|
|
||||||
# TODO: Display user login in top panel: http://stackoverflow.com/questions/806835/django-redirect-to-previous-page-after-login
|
|
||||||
# TODO: Figure how out references and named urls need to work nicely
|
# TODO: Figure how out references and named urls need to work nicely
|
||||||
# TODO: POST on existing 404 URL, PUT on existing 404 URL
|
# TODO: POST on existing 404 URL, PUT on existing 404 URL
|
||||||
#
|
#
|
||||||
|
|
|
@ -5,6 +5,11 @@
|
||||||
<head>
|
<head>
|
||||||
<style>
|
<style>
|
||||||
pre {border: 1px solid black; padding: 1em; background: #ffd}
|
pre {border: 1px solid black; padding: 1em; background: #ffd}
|
||||||
|
body {margin: 0; border:0; padding: 0;}
|
||||||
|
span.api {margin: 0.5em 1em}
|
||||||
|
span.auth {float: right; margin-right: 1em}
|
||||||
|
div.header {margin: 0; border:0; padding: 0.25em 0; background: #ddf}
|
||||||
|
div.content {margin: 0 1em;}
|
||||||
div.action {border: 1px solid black; padding: 0.5em 1em; margin-bottom: 0.5em; background: #ddf}
|
div.action {border: 1px solid black; padding: 0.5em 1em; margin-bottom: 0.5em; background: #ddf}
|
||||||
ul.accepttypes {float: right; list-style-type: none; margin: 0; padding: 0}
|
ul.accepttypes {float: right; list-style-type: none; margin: 0; padding: 0}
|
||||||
ul.accepttypes li {display: inline;}
|
ul.accepttypes li {display: inline;}
|
||||||
|
@ -17,82 +22,87 @@
|
||||||
<title>API - {{ resource.name }}</title>
|
<title>API - {{ resource.name }}</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>{{ resource.name }}</h1>
|
<div class='header'>
|
||||||
<p>{{ resource.description|linebreaksbr }}</p>
|
<span class='api'><a href='http://www.thewebhaswon.com/flywheel/'>FlyWheel API</a></span>
|
||||||
<pre><b>{{ response.status }} {{ response.status_text }}</b>{% autoescape off %}
|
<span class='auth'>{% if user.is_active %}Welcome, {{ user }}.{% if logout_url %} <a href='{{ logout_url }}'>Log out</a>{% endif %}{% else %}Not logged in {% if login_url %}<a href='{{ login_url }}'>Log in</a>{% endif %}{% endif %}</span>
|
||||||
{% for key, val in response.headers.items %}<b>{{ key }}:</b> {{ val|urlize_quoted_links }}
|
|
||||||
{% endfor %}
|
|
||||||
{{ content|urlize_quoted_links }} </pre>{% endautoescape %}
|
|
||||||
|
|
||||||
{% if 'GET' in resource.allowed_methods %}
|
|
||||||
<div class='action'>
|
|
||||||
<a href='{{ request.path }}'>GET</a>
|
|
||||||
<ul class="accepttypes">
|
|
||||||
{% for media_type in resource.emitted_media_types %}
|
|
||||||
{% with resource.ACCEPT_QUERY_PARAM|add:"="|add:media_type as param %}
|
|
||||||
<li>[<a href='{{ request.path|add_query_param:param }}'>{{ media_type }}</a>]</li>
|
|
||||||
{% endwith %}
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
<div class="clearing"></div>
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
<div class='content'>
|
||||||
|
<h1>{{ resource.name }}</h1>
|
||||||
{% comment %} *** Only display the POST/PUT/DELETE forms if we have a bound form, and if method ***
|
<p>{{ resource.description|linebreaksbr }}</p>
|
||||||
*** tunneling via POST forms is enabled. ***
|
<pre><b>{{ response.status }} {{ response.status_text }}</b>{% autoescape off %}
|
||||||
*** (We could display only the POST form if method tunneling is disabled, but I think ***
|
{% for key, val in response.headers.items %}<b>{{ key }}:</b> {{ val|urlize_quoted_links }}
|
||||||
*** the user experience would be confusing, so we simply turn all forms off. *** {% endcomment %}
|
{% endfor %}
|
||||||
|
{{ content|urlize_quoted_links }}</pre>{% endautoescape %}
|
||||||
{% if resource.METHOD_PARAM and form %}
|
|
||||||
{% if 'POST' in resource.allowed_methods %}
|
{% if 'GET' in resource.allowed_methods %}
|
||||||
<div class='action'>
|
<div class='action'>
|
||||||
<form action="{{ request.path }}" method="post">
|
<a href='{{ request.path }}'>GET</a>
|
||||||
{% csrf_token %}
|
<ul class="accepttypes">
|
||||||
{{ form.non_field_errors }}
|
{% for media_type in resource.emitted_media_types %}
|
||||||
{% for field in form %}
|
{% with resource.ACCEPT_QUERY_PARAM|add:"="|add:media_type as param %}
|
||||||
<div>
|
<li>[<a href='{{ request.path|add_query_param:param }}'>{{ media_type }}</a>]</li>
|
||||||
{{ field.label_tag }}:
|
{% endwith %}
|
||||||
{{ field }}
|
{% endfor %}
|
||||||
{{ field.help_text }}
|
</ul>
|
||||||
{{ field.errors }}
|
<div class="clearing"></div>
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
<div class="clearing"></div>
|
|
||||||
<input type="submit" value="POST" />
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if 'PUT' in resource.allowed_methods %}
|
{% comment %} *** Only display the POST/PUT/DELETE forms if we have a bound form, and if method ***
|
||||||
<div class='action'>
|
*** tunneling via POST forms is enabled. ***
|
||||||
<form action="{{ request.path }}" method="post">
|
*** (We could display only the POST form if method tunneling is disabled, but I think ***
|
||||||
<input type="hidden" name="{{ resource.METHOD_PARAM }}" value="PUT" />
|
*** the user experience would be confusing, so we simply turn all forms off. *** {% endcomment %}
|
||||||
{% csrf_token %}
|
|
||||||
{{ form.non_field_errors }}
|
|
||||||
{% for field in form %}
|
|
||||||
<div>
|
|
||||||
{{ field.label_tag }}:
|
|
||||||
{{ field }}
|
|
||||||
{{ field.help_text }}
|
|
||||||
{{ field.errors }}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
<div class="clearing"></div>
|
|
||||||
<input type="submit" value="PUT" />
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if 'DELETE' in resource.allowed_methods %}
|
{% if resource.METHOD_PARAM and form %}
|
||||||
<div class='action'>
|
{% if 'POST' in resource.allowed_methods %}
|
||||||
<form action="{{ request.path }}" method="post">
|
<div class='action'>
|
||||||
{% csrf_token %}
|
<form action="{{ request.path }}" method="post">
|
||||||
<input type="hidden" name="{{ resource.METHOD_PARAM }}" value="DELETE" />
|
{% csrf_token %}
|
||||||
<input type="submit" value="DELETE" />
|
{{ form.non_field_errors }}
|
||||||
</form>
|
{% for field in form %}
|
||||||
</div>
|
<div>
|
||||||
|
{{ field.label_tag }}:
|
||||||
|
{{ field }}
|
||||||
|
{{ field.help_text }}
|
||||||
|
{{ field.errors }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<div class="clearing"></div>
|
||||||
|
<input type="submit" value="POST" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if 'PUT' in resource.allowed_methods %}
|
||||||
|
<div class='action'>
|
||||||
|
<form action="{{ request.path }}" method="post">
|
||||||
|
<input type="hidden" name="{{ resource.METHOD_PARAM }}" value="PUT" />
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form.non_field_errors }}
|
||||||
|
{% for field in form %}
|
||||||
|
<div>
|
||||||
|
{{ field.label_tag }}:
|
||||||
|
{{ field }}
|
||||||
|
{{ field.help_text }}
|
||||||
|
{{ field.errors }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<div class="clearing"></div>
|
||||||
|
<input type="submit" value="PUT" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if 'DELETE' in resource.allowed_methods %}
|
||||||
|
<div class='action'>
|
||||||
|
<form action="{{ request.path }}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="{{ resource.METHOD_PARAM }}" value="DELETE" />
|
||||||
|
<input type="submit" value="DELETE" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -2,11 +2,21 @@ import re
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from django.utils.encoding import smart_unicode
|
from django.utils.encoding import smart_unicode
|
||||||
from django.utils.xmlutils import SimplerXMLGenerator
|
from django.utils.xmlutils import SimplerXMLGenerator
|
||||||
|
from django.core.urlresolvers import resolve
|
||||||
try:
|
try:
|
||||||
import cStringIO as StringIO
|
import cStringIO as StringIO
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import StringIO
|
import StringIO
|
||||||
|
|
||||||
|
|
||||||
|
def url_resolves(url):
|
||||||
|
"""Return True if the given URL is mapped to a view in the urlconf, False otherwise."""
|
||||||
|
try:
|
||||||
|
resolve(url)
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
# From piston
|
# From piston
|
||||||
def coerce_put_post(request):
|
def coerce_put_post(request):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue
Block a user