mirror of
https://github.com/ansible/awx.git
synced 2026-03-27 13:55:04 -02:30
Merge remote branch 'upstream/master'
This commit is contained in:
@@ -168,6 +168,8 @@ class GenericAPIView(generics.GenericAPIView, APIView):
|
|||||||
actions['GET'] = serializer.metadata()
|
actions['GET'] = serializer.metadata()
|
||||||
if actions:
|
if actions:
|
||||||
ret['actions'] = actions
|
ret['actions'] = actions
|
||||||
|
if getattr(self, 'search_fields', None):
|
||||||
|
ret['search_fields'] = self.search_fields
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
class ListAPIView(generics.ListAPIView, GenericAPIView):
|
class ListAPIView(generics.ListAPIView, GenericAPIView):
|
||||||
@@ -188,6 +190,15 @@ class ListAPIView(generics.ListAPIView, GenericAPIView):
|
|||||||
})
|
})
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
@property
|
||||||
|
def search_fields(self):
|
||||||
|
fields = []
|
||||||
|
for field in self.model._meta.fields:
|
||||||
|
if field.name in ('username', 'first_name', 'last_name', 'email',
|
||||||
|
'name', 'description', 'email'):
|
||||||
|
fields.append(field.name)
|
||||||
|
return fields
|
||||||
|
|
||||||
class ListCreateAPIView(ListAPIView, generics.ListCreateAPIView):
|
class ListCreateAPIView(ListAPIView, generics.ListCreateAPIView):
|
||||||
# Base class for a list view that allows creating new objects.
|
# Base class for a list view that allows creating new objects.
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ class FieldLookupBackend(BaseFilterBackend):
|
|||||||
Filter using field lookups provided via query string parameters.
|
Filter using field lookups provided via query string parameters.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
RESERVED_NAMES = ('page', 'page_size', 'format', 'order', 'order_by')
|
RESERVED_NAMES = ('page', 'page_size', 'format', 'order', 'order_by',
|
||||||
|
'search')
|
||||||
|
|
||||||
SUPPORTED_LOOKUPS = ('exact', 'iexact', 'contains', 'icontains',
|
SUPPORTED_LOOKUPS = ('exact', 'iexact', 'contains', 'icontains',
|
||||||
'startswith', 'istartswith', 'endswith', 'iendswith',
|
'startswith', 'istartswith', 'endswith', 'iendswith',
|
||||||
@@ -109,33 +110,57 @@ class FieldLookupBackend(BaseFilterBackend):
|
|||||||
|
|
||||||
def filter_queryset(self, request, queryset, view):
|
def filter_queryset(self, request, queryset, view):
|
||||||
try:
|
try:
|
||||||
# Apply filters and excludes specified via QUERY_PARAMS.
|
# Apply filters specified via QUERY_PARAMS. Each entry in the lists
|
||||||
filters = {}
|
# below is (negate, field, value).
|
||||||
excludes = {}
|
and_filters = []
|
||||||
for key, value in request.QUERY_PARAMS.items():
|
or_filters = []
|
||||||
|
for key, values in request.QUERY_PARAMS.lists():
|
||||||
if key in self.RESERVED_NAMES:
|
if key in self.RESERVED_NAMES:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Custom __int filter suffix (internal use only).
|
# Custom __int filter suffix (internal use only).
|
||||||
|
q_int = False
|
||||||
if key.endswith('__int'):
|
if key.endswith('__int'):
|
||||||
key = key[:-5]
|
key = key[:-5]
|
||||||
value = int(value)
|
q_int = True
|
||||||
|
# Custom or__ filter prefix (or__ can precede not__).
|
||||||
|
q_or = False
|
||||||
|
if key.startswith('or__'):
|
||||||
|
key = key[4:]
|
||||||
|
q_or = True
|
||||||
# Custom not__ filter prefix.
|
# Custom not__ filter prefix.
|
||||||
q_not = False
|
q_not = False
|
||||||
if key.startswith('not__'):
|
if key.startswith('not__'):
|
||||||
key = key[5:]
|
key = key[5:]
|
||||||
q_not = True
|
q_not = True
|
||||||
|
|
||||||
# Convert value to python and add to the appropriate dict.
|
|
||||||
value = self.value_to_python(queryset.model, key, value)
|
|
||||||
if q_not:
|
|
||||||
excludes[key] = value
|
|
||||||
else:
|
|
||||||
filters[key] = value
|
|
||||||
|
|
||||||
if filters:
|
# Convert value(s) to python and add to the appropriate list.
|
||||||
queryset = queryset.filter(**filters)
|
for value in values:
|
||||||
if excludes:
|
if q_int:
|
||||||
queryset = queryset.exclude(**excludes)
|
value = int(value)
|
||||||
|
value = self.value_to_python(queryset.model, key, value)
|
||||||
|
if q_or:
|
||||||
|
or_filters.append((q_not, key, value))
|
||||||
|
else:
|
||||||
|
and_filters.append((q_not, key, value))
|
||||||
|
|
||||||
|
# Now build Q objects for database query filter.
|
||||||
|
if and_filters or or_filters:
|
||||||
|
args = []
|
||||||
|
for n, k, v in and_filters:
|
||||||
|
if n:
|
||||||
|
args.append(~Q(**{k:v}))
|
||||||
|
else:
|
||||||
|
args.append(Q(**{k:v}))
|
||||||
|
if or_filters:
|
||||||
|
q = Q()
|
||||||
|
for n,k,v in or_filters:
|
||||||
|
if n:
|
||||||
|
q |= ~Q(**{k:v})
|
||||||
|
else:
|
||||||
|
q |= Q(**{k:v})
|
||||||
|
args.append(q)
|
||||||
|
queryset = queryset.filter(*args)
|
||||||
return queryset
|
return queryset
|
||||||
except (FieldError, FieldDoesNotExist, ValueError), e:
|
except (FieldError, FieldDoesNotExist, ValueError), e:
|
||||||
raise ParseError(e.args[0])
|
raise ParseError(e.args[0])
|
||||||
|
|||||||
@@ -418,7 +418,11 @@ class Command(NoArgsCommand):
|
|||||||
self.logger = logging.getLogger('awx.main.commands.inventory_import')
|
self.logger = logging.getLogger('awx.main.commands.inventory_import')
|
||||||
self.logger.setLevel(log_levels.get(self.verbosity, 0))
|
self.logger.setLevel(log_levels.get(self.verbosity, 0))
|
||||||
handler = logging.StreamHandler()
|
handler = logging.StreamHandler()
|
||||||
formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s')
|
class Formatter(logging.Formatter):
|
||||||
|
def format(self, record):
|
||||||
|
record.relativeSeconds = record.relativeCreated / 1000.0
|
||||||
|
return super(Formatter, self).format(record)
|
||||||
|
formatter = Formatter('%(relativeSeconds)9.3f %(levelname)-8s %(message)s')
|
||||||
handler.setFormatter(formatter)
|
handler.setFormatter(formatter)
|
||||||
self.logger.addHandler(handler)
|
self.logger.addHandler(handler)
|
||||||
self.logger.propagate = False
|
self.logger.propagate = False
|
||||||
|
|||||||
@@ -47,6 +47,15 @@ a particular page of results.
|
|||||||
The `previous` and `next` links returned with the results will set these query
|
The `previous` and `next` links returned with the results will set these query
|
||||||
string parameters automatically.
|
string parameters automatically.
|
||||||
|
|
||||||
|
## Searching
|
||||||
|
|
||||||
|
Use the `search` query string parameter to perform a case-insensitive search
|
||||||
|
within all designated text fields of a model.
|
||||||
|
|
||||||
|
?search=findme
|
||||||
|
|
||||||
|
_New in AWX 1.4_
|
||||||
|
|
||||||
## Filtering
|
## Filtering
|
||||||
|
|
||||||
Any additional query string parameters may be used to filter the list of
|
Any additional query string parameters may be used to filter the list of
|
||||||
@@ -66,6 +75,14 @@ To exclude results matching certain criteria, prefix the field parameter with
|
|||||||
|
|
||||||
?not__field=value
|
?not__field=value
|
||||||
|
|
||||||
|
(_New in AWX 1.4_) By default, all query string filters are AND'ed together, so
|
||||||
|
only the results matching *all* filters will be returned. To combine results
|
||||||
|
matching *any* one of multiple criteria, prefix each query string parameter
|
||||||
|
with `or__`:
|
||||||
|
|
||||||
|
?or__field=value&or__field=othervalue
|
||||||
|
?or__not__field=value&or__field=othervalue
|
||||||
|
|
||||||
Field lookups may also be used for more advanced queries, by appending the
|
Field lookups may also be used for more advanced queries, by appending the
|
||||||
lookup to the field name:
|
lookup to the field name:
|
||||||
|
|
||||||
|
|||||||
@@ -125,20 +125,6 @@ class BaseTestMixin(object):
|
|||||||
))
|
))
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def check_pagination_and_size(self, data, desired_count, previous=None, next=None):
|
|
||||||
self.assertTrue('results' in data)
|
|
||||||
self.assertEqual(data['count'], desired_count)
|
|
||||||
self.assertEqual(data['previous'], previous)
|
|
||||||
self.assertEqual(data['next'], next)
|
|
||||||
|
|
||||||
def check_list_ids(self, data, queryset, check_order=False):
|
|
||||||
data_ids = [x['id'] for x in data['results']]
|
|
||||||
qs_ids = queryset.values_list('pk', flat=True)
|
|
||||||
if check_order:
|
|
||||||
self.assertEqual(tuple(data_ids), tuple(qs_ids))
|
|
||||||
else:
|
|
||||||
self.assertEqual(set(data_ids), set(qs_ids))
|
|
||||||
|
|
||||||
def setup_users(self, just_super_user=False):
|
def setup_users(self, just_super_user=False):
|
||||||
# Create a user.
|
# Create a user.
|
||||||
self.super_username = 'admin'
|
self.super_username = 'admin'
|
||||||
@@ -296,12 +282,34 @@ class BaseTestMixin(object):
|
|||||||
else:
|
else:
|
||||||
f(url, expect=401)
|
f(url, expect=401)
|
||||||
|
|
||||||
|
def check_pagination_and_size(self, data, desired_count, previous=False,
|
||||||
|
next=False):
|
||||||
|
self.assertTrue('results' in data)
|
||||||
|
self.assertEqual(data['count'], desired_count)
|
||||||
|
if previous:
|
||||||
|
self.assertTrue(data['previous'])
|
||||||
|
else:
|
||||||
|
self.assertFalse(data['previous'])
|
||||||
|
if next:
|
||||||
|
self.assertTrue(data['next'])
|
||||||
|
else:
|
||||||
|
self.assertFalse(data['next'])
|
||||||
|
|
||||||
|
def check_list_ids(self, data, queryset, check_order=False):
|
||||||
|
data_ids = [x['id'] for x in data['results']]
|
||||||
|
qs_ids = queryset.values_list('pk', flat=True)
|
||||||
|
if check_order:
|
||||||
|
self.assertEqual(tuple(data_ids), tuple(qs_ids))
|
||||||
|
else:
|
||||||
|
self.assertEqual(set(data_ids), set(qs_ids))
|
||||||
|
|
||||||
def check_get_list(self, url, user, qs, fields=None, expect=200,
|
def check_get_list(self, url, user, qs, fields=None, expect=200,
|
||||||
check_order=False):
|
check_order=False, offset=None, limit=None):
|
||||||
'''
|
'''
|
||||||
Check that the given list view URL returns results for the given user
|
Check that the given list view URL returns results for the given user
|
||||||
that match the given queryset.
|
that match the given queryset.
|
||||||
'''
|
'''
|
||||||
|
offset = offset or 0
|
||||||
with self.current_user(user):
|
with self.current_user(user):
|
||||||
if expect == 400:
|
if expect == 400:
|
||||||
self.options(url, expect=200)
|
self.options(url, expect=200)
|
||||||
@@ -311,7 +319,14 @@ class BaseTestMixin(object):
|
|||||||
response = self.get(url, expect=expect)
|
response = self.get(url, expect=expect)
|
||||||
if expect != 200:
|
if expect != 200:
|
||||||
return
|
return
|
||||||
self.check_pagination_and_size(response, qs.count())
|
total = qs.count()
|
||||||
|
if limit is not None:
|
||||||
|
if limit > 0:
|
||||||
|
qs = qs[offset:offset+limit]
|
||||||
|
else:
|
||||||
|
qs = qs.none()
|
||||||
|
self.check_pagination_and_size(response, total, offset > 0,
|
||||||
|
limit and ((offset + limit) < total))
|
||||||
self.check_list_ids(response, qs, check_order)
|
self.check_list_ids(response, qs, check_order)
|
||||||
if fields:
|
if fields:
|
||||||
for obj in response['results']:
|
for obj in response['results']:
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import urllib
|
|||||||
# Django
|
# Django
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User, Group
|
from django.contrib.auth.models import User, Group
|
||||||
|
from django.db.models import Q
|
||||||
import django.test
|
import django.test
|
||||||
from django.test.client import Client
|
from django.test.client import Client
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
@@ -423,12 +424,36 @@ class UsersTest(BaseTest):
|
|||||||
url = '%s?username__regex=%s' % (base_url, urllib.quote_plus('['))
|
url = '%s?username__regex=%s' % (base_url, urllib.quote_plus('['))
|
||||||
self.check_get_list(url, self.super_django_user, base_qs, expect=400)
|
self.check_get_list(url, self.super_django_user, base_qs, expect=400)
|
||||||
|
|
||||||
|
# Filter by multiple usernames (AND).
|
||||||
|
url = '%s?username=normal&username=nobody' % base_url
|
||||||
|
qs = base_qs.filter(username='normal', username__exact='nobody')
|
||||||
|
self.assertFalse(qs.count())
|
||||||
|
self.check_get_list(url, self.super_django_user, qs)
|
||||||
|
|
||||||
|
# Filter by multiple usernames (OR).
|
||||||
|
url = '%s?or__username=normal&or__username=nobody' % base_url
|
||||||
|
qs = base_qs.filter(Q(username='normal') | Q(username='nobody'))
|
||||||
|
self.assertTrue(qs.count())
|
||||||
|
self.check_get_list(url, self.super_django_user, qs)
|
||||||
|
|
||||||
# Exclude by username.
|
# Exclude by username.
|
||||||
url = '%s?not__username=normal' % base_url
|
url = '%s?not__username=normal' % base_url
|
||||||
qs = base_qs.exclude(username='normal')
|
qs = base_qs.exclude(username='normal')
|
||||||
self.assertTrue(qs.count())
|
self.assertTrue(qs.count())
|
||||||
self.check_get_list(url, self.super_django_user, qs)
|
self.check_get_list(url, self.super_django_user, qs)
|
||||||
|
|
||||||
|
# Exclude by multiple usernames.
|
||||||
|
url = '%s?not__username=normal¬__username=nobody' % base_url
|
||||||
|
qs = base_qs.filter(~Q(username='normal') & ~Q(username='nobody'))
|
||||||
|
self.assertTrue(qs.count())
|
||||||
|
self.check_get_list(url, self.super_django_user, qs)
|
||||||
|
|
||||||
|
# Exclude by multiple usernames with OR.
|
||||||
|
url = '%s?or__not__username=normal&or__not__username=nobody' % base_url
|
||||||
|
qs = base_qs.filter(~Q(username='normal') | ~Q(username='nobody'))
|
||||||
|
self.assertTrue(qs.count())
|
||||||
|
self.check_get_list(url, self.super_django_user, qs)
|
||||||
|
|
||||||
# Exclude by username with suffix.
|
# Exclude by username with suffix.
|
||||||
url = '%s?not__username__startswith=no' % base_url
|
url = '%s?not__username__startswith=no' % base_url
|
||||||
qs = base_qs.exclude(username__startswith='no')
|
qs = base_qs.exclude(username__startswith='no')
|
||||||
@@ -625,6 +650,52 @@ class UsersTest(BaseTest):
|
|||||||
url = u'%s?user\u2605name=normal' % base_url
|
url = u'%s?user\u2605name=normal' % base_url
|
||||||
self.check_get_list(url, self.super_django_user, base_qs, expect=400)
|
self.check_get_list(url, self.super_django_user, base_qs, expect=400)
|
||||||
|
|
||||||
|
def test_user_list_pagination(self):
|
||||||
|
base_url = reverse('main:user_list')
|
||||||
|
base_qs = User.objects.distinct()
|
||||||
|
|
||||||
|
# Check list view with page size of 1.
|
||||||
|
url = '%s?order_by=username&page_size=1' % base_url
|
||||||
|
qs = base_qs.order_by('username')
|
||||||
|
self.check_get_list(url, self.super_django_user, qs, check_order=True,
|
||||||
|
limit=1)
|
||||||
|
|
||||||
|
# Check list view with page size of 1, remaining pages.
|
||||||
|
qs = base_qs.order_by('username')
|
||||||
|
for n in xrange(1, base_qs.count()):
|
||||||
|
url = '%s?order_by=username&page_size=1&page=%d' % (base_url, n+1)
|
||||||
|
self.check_get_list(url, self.super_django_user, qs,
|
||||||
|
check_order=True, offset=n, limit=1)
|
||||||
|
|
||||||
|
# Check list view with page size of 2.
|
||||||
|
qs = base_qs.order_by('username')
|
||||||
|
for n in xrange(0, base_qs.count(), 2):
|
||||||
|
url = '%s?order_by=username&page_size=2&page=%d' % (base_url, (n/2)+1)
|
||||||
|
self.check_get_list(url, self.super_django_user, qs,
|
||||||
|
check_order=True, offset=n, limit=2)
|
||||||
|
|
||||||
|
# Check list view with page size of 0 (to allow getting count of items
|
||||||
|
# matching a given filter). # FIXME: Make this work at some point!
|
||||||
|
#url = '%s?order_by=username&page_size=0' % base_url
|
||||||
|
#qs = base_qs.order_by('username')
|
||||||
|
#self.check_get_list(url, self.super_django_user, qs, check_order=True,
|
||||||
|
# limit=0)
|
||||||
|
|
||||||
|
def test_user_list_searching(self):
|
||||||
|
base_url = reverse('main:user_list')
|
||||||
|
base_qs = User.objects.distinct()
|
||||||
|
|
||||||
|
# Check search query parameter.
|
||||||
|
url = '%s?search=no' % base_url
|
||||||
|
qs = base_qs.filter(username__icontains='no')
|
||||||
|
self.check_get_list(url, self.super_django_user, qs)
|
||||||
|
|
||||||
|
# Check search query parameter.
|
||||||
|
url = '%s?search=example' % base_url
|
||||||
|
qs = base_qs.filter(email__icontains='example')
|
||||||
|
self.check_get_list(url, self.super_django_user, qs)
|
||||||
|
|
||||||
|
|
||||||
class LdapTest(BaseTest):
|
class LdapTest(BaseTest):
|
||||||
|
|
||||||
def use_test_setting(self, name, default=None, from_name=None):
|
def use_test_setting(self, name, default=None, from_name=None):
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ REST_FRAMEWORK = {
|
|||||||
'DEFAULT_FILTER_BACKENDS': (
|
'DEFAULT_FILTER_BACKENDS': (
|
||||||
'awx.main.filters.ActiveOnlyBackend',
|
'awx.main.filters.ActiveOnlyBackend',
|
||||||
'awx.main.filters.FieldLookupBackend',
|
'awx.main.filters.FieldLookupBackend',
|
||||||
|
'rest_framework.filters.SearchFilter',
|
||||||
'awx.main.filters.OrderByBackend',
|
'awx.main.filters.OrderByBackend',
|
||||||
),
|
),
|
||||||
'DEFAULT_PARSER_CLASSES': (
|
'DEFAULT_PARSER_CLASSES': (
|
||||||
|
|||||||
236
awx/templates/rest_framework/base.html
Normal file
236
awx/templates/rest_framework/base.html
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
{# Copy of base.html from rest_framework with minor AWX change. #}
|
||||||
|
{% load url from future %}
|
||||||
|
{% load rest_framework %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
{% block head %}
|
||||||
|
|
||||||
|
{% block meta %}
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||||
|
<meta name="robots" content="NONE,NOARCHIVE" />
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
<title>{% block title %}Django REST framework{% endblock %}</title>
|
||||||
|
|
||||||
|
{% block style %}
|
||||||
|
{% block bootstrap_theme %}
|
||||||
|
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/bootstrap.min.css" %}"/>
|
||||||
|
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/bootstrap-tweaks.css" %}"/>
|
||||||
|
{% endblock %}
|
||||||
|
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/prettify.css" %}"/>
|
||||||
|
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/default.css" %}"/>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="{% block bodyclass %}{% endblock %} container">
|
||||||
|
|
||||||
|
<div class="wrapper">
|
||||||
|
|
||||||
|
{% block navbar %}
|
||||||
|
<div class="navbar {% block bootstrap_navbar_variant %}navbar-inverse{% endblock %}">
|
||||||
|
<div class="navbar-inner">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<span href="/">
|
||||||
|
{% block branding %}<a class='brand' href='http://django-rest-framework.org'>Django REST framework <span class="version">{{ version }}</span></a>{% endblock %}
|
||||||
|
</span>
|
||||||
|
<ul class="nav pull-right">
|
||||||
|
{% block userlinks %}
|
||||||
|
{% if user.is_authenticated %}
|
||||||
|
<li class="dropdown">
|
||||||
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||||
|
{{ user }}
|
||||||
|
<b class="caret"></b>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li>{% optional_logout request %}</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
{% else %}
|
||||||
|
<li>{% optional_login request %}</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block breadcrumbs %}
|
||||||
|
<ul class="breadcrumb">
|
||||||
|
{% for breadcrumb_name, breadcrumb_url in breadcrumblist %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ breadcrumb_url }}" {% if forloop.last %}class="active"{% endif %}>{{ breadcrumb_name }}</a> {% if not forloop.last %}<span class="divider">›</span>{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
<!-- Content -->
|
||||||
|
<div id="content">
|
||||||
|
|
||||||
|
{% if 'GET' in allowed_methods %}
|
||||||
|
<form id="get-form" class="pull-right">
|
||||||
|
<fieldset>
|
||||||
|
<div class="btn-group format-selection">
|
||||||
|
<a class="btn btn-primary js-tooltip" href='{{ request.get_full_path }}' rel="nofollow" title="Make a GET request on the {{ name }} resource">GET</a>
|
||||||
|
|
||||||
|
<button class="btn btn-primary dropdown-toggle js-tooltip" data-toggle="dropdown" title="Specify a format for the GET request">
|
||||||
|
<span class="caret"></span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
{% for format in available_formats %}
|
||||||
|
<li>
|
||||||
|
<a class="js-tooltip format-option" href='{% add_query_param request api_settings.URL_FORMAT_OVERRIDE format %}' rel="nofollow" title="Make a GET request on the {{ name }} resource with the format set to `{{ format }}`">{{ format }}</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if options_form %}
|
||||||
|
<form class="button-form" action="{{ request.get_full_path }}" method="POST" class="pull-right">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="OPTIONS" />
|
||||||
|
<button class="btn btn-primary js-tooltip" title="Make an OPTIONS request on the {{ name }} resource">OPTIONS</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if delete_form %}
|
||||||
|
<form class="button-form" action="{{ request.get_full_path }}" method="POST" class="pull-right">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="DELETE" />
|
||||||
|
<button class="btn btn-danger js-tooltip" title="Make a DELETE request on the {{ name }} resource">DELETE</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="content-main">
|
||||||
|
<div class="page-header"><h1>{{ name }}</h1></div>
|
||||||
|
{{ description }}
|
||||||
|
<div class="request-info" style="clear: both" >
|
||||||
|
<pre class="prettyprint"><b>{{ request.method }}</b> {{ request.get_full_path }}</pre>
|
||||||
|
</div>
|
||||||
|
<div class="response-info">
|
||||||
|
<pre class="prettyprint"><div class="meta nocode"><b>HTTP {{ response.status_code }} {{ response.status_text }}</b>{% autoescape off %}
|
||||||
|
{% for key, val in response.items %}<b>{{ key }}:</b> <span class="lit">{{ val|break_long_headers|urlize_quoted_links }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
{# Original line below had content|urlize_quoted_links; for AWX disable automatic URL creation here. #}
|
||||||
|
</div>{{ content }}</pre>{% endautoescape %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if response.status_code != 403 %}
|
||||||
|
|
||||||
|
{% if post_form or raw_data_post_form %}
|
||||||
|
<div {% if post_form %}class="tabbable"{% endif %}>
|
||||||
|
{% if post_form %}
|
||||||
|
<ul class="nav nav-tabs form-switcher">
|
||||||
|
<li><a name='html-tab' href="#object-form" data-toggle="tab">HTML form</a></li>
|
||||||
|
<li><a name='raw-tab' href="#generic-content-form" data-toggle="tab">Raw data</a></li>
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
<div class="well tab-content">
|
||||||
|
{% if post_form %}
|
||||||
|
<div class="tab-pane" id="object-form">
|
||||||
|
{% with form=post_form %}
|
||||||
|
<form action="{{ request.get_full_path }}" method="POST" enctype="multipart/form-data" class="form-horizontal">
|
||||||
|
<fieldset>
|
||||||
|
{{ post_form }}
|
||||||
|
<div class="form-actions">
|
||||||
|
<button class="btn btn-primary" title="Make a POST request on the {{ name }} resource">POST</button>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
{% endwith %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div {% if post_form %}class="tab-pane"{% endif %} id="generic-content-form">
|
||||||
|
{% with form=raw_data_post_form %}
|
||||||
|
<form action="{{ request.get_full_path }}" method="POST" class="form-horizontal">
|
||||||
|
<fieldset>
|
||||||
|
{% include "rest_framework/form.html" %}
|
||||||
|
<div class="form-actions">
|
||||||
|
<button class="btn btn-primary" title="Make a POST request on the {{ name }} resource">POST</button>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
{% endwith %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if put_form or raw_data_put_form or raw_data_patch_form %}
|
||||||
|
<div {% if put_form %}class="tabbable"{% endif %}>
|
||||||
|
{% if put_form %}
|
||||||
|
<ul class="nav nav-tabs form-switcher">
|
||||||
|
<li><a name='html-tab' href="#object-form" data-toggle="tab">HTML form</a></li>
|
||||||
|
<li><a name='raw-tab' href="#generic-content-form" data-toggle="tab">Raw data</a></li>
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
<div class="well tab-content">
|
||||||
|
{% if put_form %}
|
||||||
|
<div class="tab-pane" id="object-form">
|
||||||
|
<form action="{{ request.get_full_path }}" method="POST" enctype="multipart/form-data" class="form-horizontal">
|
||||||
|
<fieldset>
|
||||||
|
{{ put_form }}
|
||||||
|
<div class="form-actions">
|
||||||
|
<button class="btn btn-primary js-tooltip" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="PUT" title="Make a PUT request on the {{ name }} resource">PUT</button>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div {% if put_form %}class="tab-pane"{% endif %} id="generic-content-form">
|
||||||
|
{% with form=raw_data_put_or_patch_form %}
|
||||||
|
<form action="{{ request.get_full_path }}" method="POST" class="form-horizontal">
|
||||||
|
<fieldset>
|
||||||
|
{% include "rest_framework/form.html" %}
|
||||||
|
<div class="form-actions">
|
||||||
|
{% if raw_data_put_form %}
|
||||||
|
<button class="btn btn-primary js-tooltip" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="PUT" title="Make a PUT request on the {{ name }} resource">PUT</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if raw_data_patch_form %}
|
||||||
|
<button class="btn btn-primary js-tooltip" name="{{ api_settings.FORM_METHOD_OVERRIDE }}" value="PATCH" title="Make a PATCH request on the {{ name }} resource">PATCH</button>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
{% endwith %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- END content-main -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- END Content -->
|
||||||
|
|
||||||
|
<div id="push"></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div><!-- ./wrapper -->
|
||||||
|
|
||||||
|
{% block footer %}
|
||||||
|
<!--<div id="footer">
|
||||||
|
<a class="powered-by" href='http://django-rest-framework.org'>Django REST framework</a>
|
||||||
|
</div>-->
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
<script src="{% static "rest_framework/js/jquery-1.8.1-min.js" %}"></script>
|
||||||
|
<script src="{% static "rest_framework/js/bootstrap.min.js" %}"></script>
|
||||||
|
<script src="{% static "rest_framework/js/prettify-min.js" %}"></script>
|
||||||
|
<script src="{% static "rest_framework/js/default.js" %}"></script>
|
||||||
|
{% endblock %}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user