mirror of
https://github.com/ansible/awx.git
synced 2024-11-02 18:21:12 +03:00
Merge remote branch 'upstream/master'
This commit is contained in:
commit
6f6a7dbf8b
@ -168,6 +168,8 @@ class GenericAPIView(generics.GenericAPIView, APIView):
|
||||
actions['GET'] = serializer.metadata()
|
||||
if actions:
|
||||
ret['actions'] = actions
|
||||
if getattr(self, 'search_fields', None):
|
||||
ret['search_fields'] = self.search_fields
|
||||
return ret
|
||||
|
||||
class ListAPIView(generics.ListAPIView, GenericAPIView):
|
||||
@ -188,6 +190,15 @@ class ListAPIView(generics.ListAPIView, GenericAPIView):
|
||||
})
|
||||
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):
|
||||
# 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.
|
||||
'''
|
||||
|
||||
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',
|
||||
'startswith', 'istartswith', 'endswith', 'iendswith',
|
||||
@ -109,33 +110,57 @@ class FieldLookupBackend(BaseFilterBackend):
|
||||
|
||||
def filter_queryset(self, request, queryset, view):
|
||||
try:
|
||||
# Apply filters and excludes specified via QUERY_PARAMS.
|
||||
filters = {}
|
||||
excludes = {}
|
||||
for key, value in request.QUERY_PARAMS.items():
|
||||
# Apply filters specified via QUERY_PARAMS. Each entry in the lists
|
||||
# below is (negate, field, value).
|
||||
and_filters = []
|
||||
or_filters = []
|
||||
for key, values in request.QUERY_PARAMS.lists():
|
||||
if key in self.RESERVED_NAMES:
|
||||
continue
|
||||
|
||||
# Custom __int filter suffix (internal use only).
|
||||
q_int = False
|
||||
if key.endswith('__int'):
|
||||
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.
|
||||
q_not = False
|
||||
if key.startswith('not__'):
|
||||
key = key[5:]
|
||||
q_not = True
|
||||
|
||||
# Convert value to python and add to the appropriate dict.
|
||||
# Convert value(s) to python and add to the appropriate list.
|
||||
for value in values:
|
||||
if q_int:
|
||||
value = int(value)
|
||||
value = self.value_to_python(queryset.model, key, value)
|
||||
if q_not:
|
||||
excludes[key] = value
|
||||
if q_or:
|
||||
or_filters.append((q_not, key, value))
|
||||
else:
|
||||
filters[key] = value
|
||||
and_filters.append((q_not, key, value))
|
||||
|
||||
if filters:
|
||||
queryset = queryset.filter(**filters)
|
||||
if excludes:
|
||||
queryset = queryset.exclude(**excludes)
|
||||
# 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
|
||||
except (FieldError, FieldDoesNotExist, ValueError), e:
|
||||
raise ParseError(e.args[0])
|
||||
|
@ -418,7 +418,11 @@ class Command(NoArgsCommand):
|
||||
self.logger = logging.getLogger('awx.main.commands.inventory_import')
|
||||
self.logger.setLevel(log_levels.get(self.verbosity, 0))
|
||||
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)
|
||||
self.logger.addHandler(handler)
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
(_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
|
||||
lookup to the field name:
|
||||
|
||||
|
@ -125,20 +125,6 @@ class BaseTestMixin(object):
|
||||
))
|
||||
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):
|
||||
# Create a user.
|
||||
self.super_username = 'admin'
|
||||
@ -296,12 +282,34 @@ class BaseTestMixin(object):
|
||||
else:
|
||||
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,
|
||||
check_order=False):
|
||||
check_order=False, offset=None, limit=None):
|
||||
'''
|
||||
Check that the given list view URL returns results for the given user
|
||||
that match the given queryset.
|
||||
'''
|
||||
offset = offset or 0
|
||||
with self.current_user(user):
|
||||
if expect == 400:
|
||||
self.options(url, expect=200)
|
||||
@ -311,7 +319,14 @@ class BaseTestMixin(object):
|
||||
response = self.get(url, expect=expect)
|
||||
if expect != 200:
|
||||
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)
|
||||
if fields:
|
||||
for obj in response['results']:
|
||||
|
@ -9,6 +9,7 @@ import urllib
|
||||
# Django
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.db.models import Q
|
||||
import django.test
|
||||
from django.test.client import Client
|
||||
from django.core.urlresolvers import reverse
|
||||
@ -423,12 +424,36 @@ class UsersTest(BaseTest):
|
||||
url = '%s?username__regex=%s' % (base_url, urllib.quote_plus('['))
|
||||
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.
|
||||
url = '%s?not__username=normal' % base_url
|
||||
qs = base_qs.exclude(username='normal')
|
||||
self.assertTrue(qs.count())
|
||||
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.
|
||||
url = '%s?not__username__startswith=no' % base_url
|
||||
qs = base_qs.exclude(username__startswith='no')
|
||||
@ -625,6 +650,52 @@ class UsersTest(BaseTest):
|
||||
url = u'%s?user\u2605name=normal' % base_url
|
||||
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):
|
||||
|
||||
def use_test_setting(self, name, default=None, from_name=None):
|
||||
|
@ -150,6 +150,7 @@ REST_FRAMEWORK = {
|
||||
'DEFAULT_FILTER_BACKENDS': (
|
||||
'awx.main.filters.ActiveOnlyBackend',
|
||||
'awx.main.filters.FieldLookupBackend',
|
||||
'rest_framework.filters.SearchFilter',
|
||||
'awx.main.filters.OrderByBackend',
|
||||
),
|
||||
'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>
|
Loading…
Reference in New Issue
Block a user