commit
2ec2722c67
|
|
@ -3,24 +3,11 @@ import json
|
||||||
from django import VERSION
|
from django import VERSION
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.conf.urls import url
|
||||||
try:
|
|
||||||
from django.conf.urls import url
|
|
||||||
except ImportError:
|
|
||||||
# Django < 1.4
|
|
||||||
from django.conf.urls.defaults import url
|
|
||||||
|
|
||||||
from django.contrib.admin import ModelAdmin, TabularInline, StackedInline
|
from django.contrib.admin import ModelAdmin, TabularInline, StackedInline
|
||||||
from django.contrib.admin.options import InlineModelAdmin
|
from django.contrib.admin.options import InlineModelAdmin
|
||||||
|
from django.contrib.contenttypes.admin import (GenericStackedInline,
|
||||||
try:
|
|
||||||
from django.contrib.contenttypes.admin import (GenericStackedInline,
|
|
||||||
GenericTabularInline)
|
GenericTabularInline)
|
||||||
except:
|
|
||||||
# Django < 1.7
|
|
||||||
from django.contrib.contenttypes.generic import (GenericStackedInline,
|
|
||||||
GenericTabularInline)
|
|
||||||
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.http import HttpResponse, Http404
|
from django.http import HttpResponse, Http404
|
||||||
|
|
@ -51,13 +38,7 @@ class SortableAdminBase(object):
|
||||||
its sort order can be changed. This view adds a link to the
|
its sort order can be changed. This view adds a link to the
|
||||||
object_tools block to take people to the view to change the sorting.
|
object_tools block to take people to the view to change the sorting.
|
||||||
"""
|
"""
|
||||||
|
if get_is_sortable(self.get_queryset(request)):
|
||||||
try:
|
|
||||||
qs_method = getattr(self, 'get_queryset', self.queryset)
|
|
||||||
except AttributeError:
|
|
||||||
qs_method = self.get_queryset
|
|
||||||
|
|
||||||
if get_is_sortable(qs_method(request)):
|
|
||||||
self.change_list_template = \
|
self.change_list_template = \
|
||||||
self.sortable_change_list_with_sort_link_template
|
self.sortable_change_list_with_sort_link_template
|
||||||
self.is_sortable = True
|
self.is_sortable = True
|
||||||
|
|
@ -101,12 +82,7 @@ class SortableAdmin(SortableAdminBase, ModelAdmin):
|
||||||
|
|
||||||
def get_urls(self):
|
def get_urls(self):
|
||||||
urls = super(SortableAdmin, self).get_urls()
|
urls = super(SortableAdmin, self).get_urls()
|
||||||
opts = self.model._meta
|
info = self.model._meta.app_label, self.model._meta.model_name
|
||||||
try:
|
|
||||||
info = opts.app_label, opts.model_name
|
|
||||||
except AttributeError:
|
|
||||||
# Django < 1.7
|
|
||||||
info = opts.app_label, opts.model_name
|
|
||||||
|
|
||||||
# this ajax view changes the order of instances of the model type
|
# this ajax view changes the order of instances of the model type
|
||||||
admin_do_sorting_url = url(
|
admin_do_sorting_url = url(
|
||||||
|
|
@ -150,11 +126,7 @@ class SortableAdmin(SortableAdminBase, ModelAdmin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Apply any sort filters to create a subset of sortable objects
|
# Apply any sort filters to create a subset of sortable objects
|
||||||
try:
|
objects = self.get_queryset(request).filter(**filters)
|
||||||
qs_method = getattr(self, 'get_queryset', self.queryset)
|
|
||||||
except AttributeError:
|
|
||||||
qs_method = self.get_queryset
|
|
||||||
objects = qs_method(request).filter(**filters)
|
|
||||||
|
|
||||||
# Determine if we need to regroup objects relative to a
|
# Determine if we need to regroup objects relative to a
|
||||||
# foreign key specified on the model class that is extending Sortable.
|
# foreign key specified on the model class that is extending Sortable.
|
||||||
|
|
@ -169,6 +141,10 @@ class SortableAdmin(SortableAdminBase, ModelAdmin):
|
||||||
|
|
||||||
for field in self.model._meta.fields:
|
for field in self.model._meta.fields:
|
||||||
if isinstance(field, SortableForeignKey):
|
if isinstance(field, SortableForeignKey):
|
||||||
|
try:
|
||||||
|
sortable_by_fk = field.remote_field.model
|
||||||
|
except AttributeError:
|
||||||
|
# Django < 1.9
|
||||||
sortable_by_fk = field.rel.to
|
sortable_by_fk = field.rel.to
|
||||||
sortable_by_field_name = field.name.lower()
|
sortable_by_field_name = field.name.lower()
|
||||||
sortable_by_class_is_sortable = sortable_by_fk.objects.count() >= 2
|
sortable_by_class_is_sortable = sortable_by_fk.objects.count() >= 2
|
||||||
|
|
@ -206,9 +182,6 @@ class SortableAdmin(SortableAdminBase, ModelAdmin):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
order_field_name = opts.model._meta.ordering[0]
|
order_field_name = opts.model._meta.ordering[0]
|
||||||
except (AttributeError, IndexError):
|
|
||||||
# for Django 1.5.x
|
|
||||||
order_field_name = opts.ordering[0]
|
|
||||||
except (AttributeError, IndexError):
|
except (AttributeError, IndexError):
|
||||||
order_field_name = 'order'
|
order_field_name = 'order'
|
||||||
|
|
||||||
|
|
@ -328,27 +301,18 @@ class SortableInlineBase(SortableAdminBase, InlineModelAdmin):
|
||||||
' (or Sortable for legacy implementations)')
|
' (or Sortable for legacy implementations)')
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
if VERSION < (1, 6):
|
|
||||||
qs = super(SortableInlineBase, self).queryset(request)
|
|
||||||
else:
|
|
||||||
qs = super(SortableInlineBase, self).get_queryset(request)
|
qs = super(SortableInlineBase, self).get_queryset(request)
|
||||||
|
|
||||||
if get_is_sortable(qs):
|
if get_is_sortable(qs):
|
||||||
self.model.is_sortable = True
|
self.model.is_sortable = True
|
||||||
else:
|
else:
|
||||||
self.model.is_sortable = False
|
self.model.is_sortable = False
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
if VERSION < (1, 6):
|
|
||||||
queryset = get_queryset
|
|
||||||
|
|
||||||
|
|
||||||
class SortableTabularInline(TabularInline, SortableInlineBase):
|
class SortableTabularInline(TabularInline, SortableInlineBase):
|
||||||
"""Custom template that enables sorting for tabular inlines"""
|
"""Custom template that enables sorting for tabular inlines"""
|
||||||
if VERSION >= (1, 10):
|
if VERSION >= (1, 10):
|
||||||
template = 'adminsortable/edit_inline/tabular-1.10.x.html'
|
template = 'adminsortable/edit_inline/tabular-1.10.x.html'
|
||||||
elif VERSION < (1, 6):
|
|
||||||
template = 'adminsortable/edit_inline/tabular-1.5.x.html'
|
|
||||||
else:
|
else:
|
||||||
template = 'adminsortable/edit_inline/tabular.html'
|
template = 'adminsortable/edit_inline/tabular.html'
|
||||||
|
|
||||||
|
|
@ -357,8 +321,6 @@ class SortableStackedInline(StackedInline, SortableInlineBase):
|
||||||
"""Custom template that enables sorting for stacked inlines"""
|
"""Custom template that enables sorting for stacked inlines"""
|
||||||
if VERSION >= (1, 10):
|
if VERSION >= (1, 10):
|
||||||
template = 'adminsortable/edit_inline/stacked-1.10.x.html'
|
template = 'adminsortable/edit_inline/stacked-1.10.x.html'
|
||||||
elif VERSION < (1, 6):
|
|
||||||
template = 'adminsortable/edit_inline/stacked-1.5.x.html'
|
|
||||||
else:
|
else:
|
||||||
template = 'adminsortable/edit_inline/stacked.html'
|
template = 'adminsortable/edit_inline/stacked.html'
|
||||||
|
|
||||||
|
|
@ -367,8 +329,6 @@ class SortableGenericTabularInline(GenericTabularInline, SortableInlineBase):
|
||||||
"""Custom template that enables sorting for tabular inlines"""
|
"""Custom template that enables sorting for tabular inlines"""
|
||||||
if VERSION >= (1, 10):
|
if VERSION >= (1, 10):
|
||||||
template = 'adminsortable/edit_inline/tabular-1.10.x.html'
|
template = 'adminsortable/edit_inline/tabular-1.10.x.html'
|
||||||
elif VERSION < (1, 6):
|
|
||||||
template = 'adminsortable/edit_inline/tabular-1.5.x.html'
|
|
||||||
else:
|
else:
|
||||||
template = 'adminsortable/edit_inline/tabular.html'
|
template = 'adminsortable/edit_inline/tabular.html'
|
||||||
|
|
||||||
|
|
@ -377,7 +337,5 @@ class SortableGenericStackedInline(GenericStackedInline, SortableInlineBase):
|
||||||
"""Custom template that enables sorting for stacked inlines"""
|
"""Custom template that enables sorting for stacked inlines"""
|
||||||
if VERSION >= (1, 10):
|
if VERSION >= (1, 10):
|
||||||
template = 'adminsortable/edit_inline/stacked-1.10.x.html'
|
template = 'adminsortable/edit_inline/stacked-1.10.x.html'
|
||||||
elif VERSION < (1, 6):
|
|
||||||
template = 'adminsortable/edit_inline/stacked-1.5.x.html'
|
|
||||||
else:
|
else:
|
||||||
template = 'adminsortable/edit_inline/stacked.html'
|
template = 'adminsortable/edit_inline/stacked.html'
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,4 @@ class SortableForeignKey(ForeignKey):
|
||||||
This field replaces previous functionality where `sortable_by` was
|
This field replaces previous functionality where `sortable_by` was
|
||||||
defined as a model property that specified another model class.
|
defined as a model property that specified another model class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def south_field_triple(self):
|
|
||||||
try:
|
|
||||||
from south.modelsinspector import introspector
|
|
||||||
cls_name = '{0}.{1}'.format(
|
|
||||||
self.__class__.__module__,
|
|
||||||
self.__class__.__name__)
|
|
||||||
args, kwargs = introspector(self)
|
|
||||||
return cls_name, args, kwargs
|
|
||||||
except ImportError:
|
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -1,92 +0,0 @@
|
||||||
{% load i18n admin_modify adminsortable_tags admin_urls %}
|
|
||||||
{% load static from staticfiles %}
|
|
||||||
<div class="inline-group" id="{{ inline_admin_formset.formset.prefix }}-group">
|
|
||||||
<h2>{{ inline_admin_formset.opts.verbose_name_plural|title }} {% if inline_admin_formset.formset.initial_form_count > 1 %} - {% trans "drag and drop to change order" %}{% endif %}</h2>
|
|
||||||
{{ inline_admin_formset.formset.management_form }}
|
|
||||||
{{ inline_admin_formset.formset.non_form_errors }}
|
|
||||||
|
|
||||||
{% for inline_admin_form in inline_admin_formset %}<div class="inline-related{% if inline_admin_form.original %} has_original{% endif %}{% if forloop.last %} empty-form last-related{% endif %}" id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}">
|
|
||||||
<h3>
|
|
||||||
{% if inline_admin_form.original %}
|
|
||||||
{% with initial_forms_count=inline_admin_formset.formset.management_form.INITIAL_FORMS.value %}
|
|
||||||
<i class="fa fa-{% if forloop.first %}sort-desc{% elif forloop.counter == initial_forms_count %}sort-asc{% else %}sort{% endif %}"></i>
|
|
||||||
{% endwith %}
|
|
||||||
{% endif %}
|
|
||||||
<b>{{ inline_admin_formset.opts.verbose_name|title }}:</b> <span class="inline_label">{% if inline_admin_form.original %}{{ inline_admin_form.original }}{% else %}#{{ forloop.counter }}{% endif %}</span>
|
|
||||||
{% if inline_admin_form.show_url %}<a href="../../../r/{{ inline_admin_form.original_content_type_id }}/{{ inline_admin_form.original.id }}/">{% trans "View on site" %}</a>{% endif %}
|
|
||||||
{% if inline_admin_formset.formset.can_delete and inline_admin_form.original %}<span class="delete">{{ inline_admin_form.deletion_field.field }} {{ inline_admin_form.deletion_field.label_tag }}</span>{% endif %}
|
|
||||||
</h3>
|
|
||||||
{% if inline_admin_form.form.non_field_errors %}{{ inline_admin_form.form.non_field_errors }}{% endif %}
|
|
||||||
{% for fieldset in inline_admin_form %}
|
|
||||||
{% include "admin/includes/fieldset.html" %}
|
|
||||||
{% endfor %}
|
|
||||||
{% if inline_admin_form.has_auto_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
|
|
||||||
{{ inline_admin_form.fk_field.field }}
|
|
||||||
{% if inline_admin_form.original %}
|
|
||||||
<input type="hidden" name="admin_sorting_url" value="{% url opts|admin_urlname:'do_sorting' inline_admin_form.original.model_type_id %}" />
|
|
||||||
{% endif %}
|
|
||||||
</div>{% endfor %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
(function($) {
|
|
||||||
$(document).ready(function() {
|
|
||||||
var rows = "#{{ inline_admin_formset.formset.prefix }}-group .inline-related";
|
|
||||||
var updateInlineLabel = function(row) {
|
|
||||||
$(rows).find(".inline_label").each(function(i) {
|
|
||||||
var count = i + 1;
|
|
||||||
$(this).html($(this).html().replace(/(#\d+)/g, "#" + count));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
var reinitDateTimeShortCuts = function() {
|
|
||||||
// Reinitialize the calendar and clock widgets by force, yuck.
|
|
||||||
if (typeof DateTimeShortcuts != "undefined") {
|
|
||||||
$(".datetimeshortcuts").remove();
|
|
||||||
DateTimeShortcuts.init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var updateSelectFilter = function() {
|
|
||||||
// If any SelectFilter widgets were added, instantiate a new instance.
|
|
||||||
if (typeof SelectFilter != "undefined"){
|
|
||||||
$(".selectfilter").each(function(index, value){
|
|
||||||
var namearr = value.name.split('-');
|
|
||||||
SelectFilter.init(value.id, namearr[namearr.length-1], false, "{% static 'admin/' %}");
|
|
||||||
});
|
|
||||||
$(".selectfilterstacked").each(function(index, value){
|
|
||||||
var namearr = value.name.split('-');
|
|
||||||
SelectFilter.init(value.id, namearr[namearr.length-1], true, "{% static 'admin/' %}");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var initPrepopulatedFields = function(row) {
|
|
||||||
row.find('.prepopulated_field').each(function() {
|
|
||||||
var field = $(this);
|
|
||||||
var input = field.find('input, select, textarea');
|
|
||||||
var dependency_list = input.data('dependency_list') || [];
|
|
||||||
var dependencies = [];
|
|
||||||
$.each(dependency_list, function(i, field_name) {
|
|
||||||
dependencies.push('#' + row.find(field_name).find('input, select, textarea').attr('id'));
|
|
||||||
});
|
|
||||||
if (dependencies.length) {
|
|
||||||
input.prepopulate(dependencies, input.attr('maxlength'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
$(rows).formset({
|
|
||||||
prefix: "{{ inline_admin_formset.formset.prefix }}",
|
|
||||||
addText: "{% blocktrans with inline_admin_formset.opts.verbose_name|title as verbose_name %}Add another {{ verbose_name }}{% endblocktrans %}",
|
|
||||||
formCssClass: "dynamic-{{ inline_admin_formset.formset.prefix }}",
|
|
||||||
deleteCssClass: "inline-deletelink",
|
|
||||||
deleteText: "{% trans "Remove" %}",
|
|
||||||
emptyCssClass: "empty-form",
|
|
||||||
removed: updateInlineLabel,
|
|
||||||
added: (function(row) {
|
|
||||||
initPrepopulatedFields(row);
|
|
||||||
reinitDateTimeShortCuts();
|
|
||||||
updateSelectFilter();
|
|
||||||
updateInlineLabel(row);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})(django.jQuery);
|
|
||||||
</script>
|
|
||||||
|
|
@ -1,136 +0,0 @@
|
||||||
{% load i18n admin_modify adminsortable_tags admin_urls %}
|
|
||||||
{% load static from staticfiles %}
|
|
||||||
<div class="inline-group" id="{{ inline_admin_formset.formset.prefix }}-group">
|
|
||||||
<div class="tabular inline-related {% if forloop.last %}last-related{% endif %}">
|
|
||||||
{{ inline_admin_formset.formset.management_form }}
|
|
||||||
<fieldset class="module">
|
|
||||||
<h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst }} {% if inline_admin_formset.formset.initial_form_count > 1 %} - {% trans "drag and drop to change order" %}{% endif %}</h2>
|
|
||||||
{{ inline_admin_formset.formset.non_form_errors }}
|
|
||||||
<table>
|
|
||||||
<thead><tr>
|
|
||||||
{% for field in inline_admin_formset.fields %}
|
|
||||||
{% if not field.widget.is_hidden %}
|
|
||||||
<th{% if forloop.first %} colspan="2"{% endif %}{% if field.required %} class="required"{% endif %}>{{ field.label|capfirst }}</th>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{% if inline_admin_formset.formset.can_delete %}<th>{% trans "Delete?" %}</th>{% endif %}
|
|
||||||
</tr></thead>
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
{% for inline_admin_form in inline_admin_formset %}
|
|
||||||
{% if inline_admin_form.form.non_field_errors %}
|
|
||||||
<tr><td colspan="{{ inline_admin_form|cell_count }}">{{ inline_admin_form.form.non_field_errors }}</td></tr>
|
|
||||||
{% endif %}
|
|
||||||
<tr class="{% cycle "row1" "row2" %} {% if inline_admin_form.original or inline_admin_form.show_url %}has_original{% endif %}{% if forloop.last %} empty-form{% endif %}"
|
|
||||||
id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}">
|
|
||||||
<td class="original">
|
|
||||||
|
|
||||||
{% if inline_admin_form.original or inline_admin_form.show_url %}<p>
|
|
||||||
{% with initial_forms_count=inline_admin_form.formset.management_form.INITIAL_FORMS.value %}
|
|
||||||
<i class="fa fa-{% if forloop.first %}sort-desc{% elif forloop.counter == initial_forms_count %}sort-asc{% else %}sort{% endif %}"></i>
|
|
||||||
{% endwith %}
|
|
||||||
{% if inline_admin_form.original %} {{ inline_admin_form.original }}{% endif %}
|
|
||||||
{% if inline_admin_form.show_url %}<a href="../../../r/{{ inline_admin_form.original_content_type_id }}/{{ inline_admin_form.original.id }}/">{% trans "View on site" %}</a>{% endif %}
|
|
||||||
</p>{% endif %}
|
|
||||||
{% if inline_admin_form.has_auto_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
|
|
||||||
{{ inline_admin_form.fk_field.field }}
|
|
||||||
{% spaceless %}
|
|
||||||
{% for fieldset in inline_admin_form %}
|
|
||||||
{% for line in fieldset %}
|
|
||||||
{% for field in line %}
|
|
||||||
{% if field.is_hidden %} {{ field.field }} {% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{% endfor %}
|
|
||||||
{% endfor %}
|
|
||||||
{% endspaceless %}
|
|
||||||
{% if inline_admin_form.original %}
|
|
||||||
<input type="hidden" name="admin_sorting_url" value="{% url opts|admin_urlname:'do_sorting' inline_admin_form.original.model_type_id %}" />
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
{% for fieldset in inline_admin_form %}
|
|
||||||
{% for line in fieldset %}
|
|
||||||
{% for field in line %}
|
|
||||||
<td class="{{ field.field.name }}">
|
|
||||||
{% if field.is_readonly %}
|
|
||||||
<p>{{ field.contents }}</p>
|
|
||||||
{% else %}
|
|
||||||
{{ field.field.errors.as_ul }}
|
|
||||||
{{ field.field }}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
{% endfor %}
|
|
||||||
{% endfor %}
|
|
||||||
{% endfor %}
|
|
||||||
{% if inline_admin_formset.formset.can_delete %}
|
|
||||||
<td class="delete">{% if inline_admin_form.original %}{{ inline_admin_form.deletion_field.field }}{% endif %}</td>
|
|
||||||
{% endif %}
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</fieldset>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
(function($) {
|
|
||||||
$(document).ready(function($) {
|
|
||||||
var rows = "#{{ inline_admin_formset.formset.prefix }}-group .tabular.inline-related tbody tr";
|
|
||||||
var alternatingRows = function(row) {
|
|
||||||
$(rows).not(".add-row").removeClass("row1 row2")
|
|
||||||
.filter(":even").addClass("row1").end()
|
|
||||||
.filter(rows + ":odd").addClass("row2");
|
|
||||||
}
|
|
||||||
var reinitDateTimeShortCuts = function() {
|
|
||||||
// Reinitialize the calendar and clock widgets by force
|
|
||||||
if (typeof DateTimeShortcuts != "undefined") {
|
|
||||||
$(".datetimeshortcuts").remove();
|
|
||||||
DateTimeShortcuts.init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var updateSelectFilter = function() {
|
|
||||||
// If any SelectFilter widgets are a part of the new form,
|
|
||||||
// instantiate a new SelectFilter instance for it.
|
|
||||||
if (typeof SelectFilter != "undefined"){
|
|
||||||
$(".selectfilter").each(function(index, value){
|
|
||||||
var namearr = value.name.split('-');
|
|
||||||
SelectFilter.init(value.id, namearr[namearr.length-1], false, "{% static 'admin/' %}");
|
|
||||||
});
|
|
||||||
$(".selectfilterstacked").each(function(index, value){
|
|
||||||
var namearr = value.name.split('-');
|
|
||||||
SelectFilter.init(value.id, namearr[namearr.length-1], true, "{% static 'admin/' %}");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var initPrepopulatedFields = function(row) {
|
|
||||||
row.find('.prepopulated_field').each(function() {
|
|
||||||
var field = $(this);
|
|
||||||
var input = field.find('input, select, textarea');
|
|
||||||
var dependency_list = input.data('dependency_list') || [];
|
|
||||||
var dependencies = [];
|
|
||||||
$.each(dependency_list, function(i, field_name) {
|
|
||||||
dependencies.push('#' + row.find(field_name).find('input, select, textarea').attr('id'));
|
|
||||||
});
|
|
||||||
if (dependencies.length) {
|
|
||||||
input.prepopulate(dependencies, input.attr('maxlength'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
$(rows).formset({
|
|
||||||
prefix: "{{ inline_admin_formset.formset.prefix }}",
|
|
||||||
addText: "{% blocktrans with inline_admin_formset.opts.verbose_name|title as verbose_name %}Add another {{ verbose_name }}{% endblocktrans %}",
|
|
||||||
formCssClass: "dynamic-{{ inline_admin_formset.formset.prefix }}",
|
|
||||||
deleteCssClass: "inline-deletelink",
|
|
||||||
deleteText: "{% trans "Remove" %}",
|
|
||||||
emptyCssClass: "empty-form",
|
|
||||||
removed: alternatingRows,
|
|
||||||
added: (function(row) {
|
|
||||||
initPrepopulatedFields(row);
|
|
||||||
reinitDateTimeShortCuts();
|
|
||||||
updateSelectFilter();
|
|
||||||
alternatingRows(row);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})(django.jQuery);
|
|
||||||
</script>
|
|
||||||
|
|
@ -2,11 +2,6 @@ from itertools import groupby
|
||||||
|
|
||||||
import django
|
import django
|
||||||
from django import template
|
from django import template
|
||||||
try:
|
|
||||||
from django import TemplateSyntaxError
|
|
||||||
except ImportError:
|
|
||||||
#support for django 1.3
|
|
||||||
from django.template.base import TemplateSyntaxError
|
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
|
@ -64,14 +59,15 @@ def dynamic_regroup(parser, token):
|
||||||
"""
|
"""
|
||||||
firstbits = token.contents.split(None, 3)
|
firstbits = token.contents.split(None, 3)
|
||||||
if len(firstbits) != 4:
|
if len(firstbits) != 4:
|
||||||
raise TemplateSyntaxError("'regroup' tag takes five arguments")
|
raise template.TemplateSyntaxError("'regroup' tag takes five arguments")
|
||||||
target = parser.compile_filter(firstbits[1])
|
target = parser.compile_filter(firstbits[1])
|
||||||
if firstbits[2] != 'by':
|
if firstbits[2] != 'by':
|
||||||
raise TemplateSyntaxError("second argument to 'regroup' tag must be 'by'")
|
raise template.TemplateSyntaxError(
|
||||||
|
"second argument to 'regroup' tag must be 'by'")
|
||||||
lastbits_reversed = firstbits[3][::-1].split(None, 2)
|
lastbits_reversed = firstbits[3][::-1].split(None, 2)
|
||||||
if lastbits_reversed[1][::-1] != 'as':
|
if lastbits_reversed[1][::-1] != 'as':
|
||||||
raise TemplateSyntaxError("next-to-last argument to 'regroup' tag must"
|
raise template.TemplateSyntaxError(
|
||||||
" be 'as'")
|
"next-to-last argument to 'regroup' tag must be 'as'")
|
||||||
|
|
||||||
expression = lastbits_reversed[2][::-1]
|
expression = lastbits_reversed[2][::-1]
|
||||||
var_name = lastbits_reversed[0][::-1]
|
var_name = lastbits_reversed[0][::-1]
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ class ComponentInline(SortableStackedInline):
|
||||||
# )
|
# )
|
||||||
model = Component
|
model = Component
|
||||||
|
|
||||||
def queryset(self, request):
|
def get_queryset(self, request):
|
||||||
qs = super(ComponentInline, self).queryset(
|
qs = super(ComponentInline, self).get_queryset(
|
||||||
request).exclude(title__icontains='2')
|
request).exclude(title__icontains='2')
|
||||||
if get_is_sortable(qs):
|
if get_is_sortable(qs):
|
||||||
self.model.is_sortable = True
|
self.model.is_sortable = True
|
||||||
|
|
@ -37,14 +37,14 @@ class ComponentInline(SortableStackedInline):
|
||||||
|
|
||||||
|
|
||||||
class WidgetAdmin(SortableAdmin):
|
class WidgetAdmin(SortableAdmin):
|
||||||
def queryset(self, request):
|
def get_queryset(self, request):
|
||||||
"""
|
"""
|
||||||
A simple example demonstrating that adminsortable works even in
|
A simple example demonstrating that adminsortable works even in
|
||||||
situations where you need to filter the queryset in admin. Here,
|
situations where you need to filter the queryset in admin. Here,
|
||||||
we are just filtering out `widget` instances with an pk higher
|
we are just filtering out `widget` instances with an pk higher
|
||||||
than 3
|
than 3
|
||||||
"""
|
"""
|
||||||
qs = super(WidgetAdmin, self).queryset(request)
|
qs = super(WidgetAdmin, self).get_queryset(request)
|
||||||
return qs.filter(id__lte=3)
|
return qs.filter(id__lte=3)
|
||||||
|
|
||||||
inlines = [ComponentInline]
|
inlines = [ComponentInline]
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,4 @@
|
||||||
from django import VERSION
|
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||||
|
|
||||||
if VERSION < (1, 9):
|
|
||||||
from django.contrib.contenttypes.generic import GenericForeignKey
|
|
||||||
else:
|
|
||||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
|
||||||
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
|
|
@ -51,7 +45,7 @@ class Project(SimpleModel, SortableMixin):
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['order']
|
ordering = ['order']
|
||||||
|
|
||||||
category = SortableForeignKey(Category)
|
category = SortableForeignKey(Category, on_delete=models.CASCADE)
|
||||||
description = models.TextField()
|
description = models.TextField()
|
||||||
|
|
||||||
order = models.PositiveIntegerField(default=0, editable=False)
|
order = models.PositiveIntegerField(default=0, editable=False)
|
||||||
|
|
@ -63,7 +57,7 @@ class Credit(SortableMixin):
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['order']
|
ordering = ['order']
|
||||||
|
|
||||||
project = models.ForeignKey(Project)
|
project = models.ForeignKey(Project, on_delete=models.CASCADE)
|
||||||
first_name = models.CharField(max_length=30, help_text="Given name")
|
first_name = models.CharField(max_length=30, help_text="Given name")
|
||||||
last_name = models.CharField(max_length=30, help_text="Family name")
|
last_name = models.CharField(max_length=30, help_text="Family name")
|
||||||
|
|
||||||
|
|
@ -79,7 +73,7 @@ class Note(SortableMixin):
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['order']
|
ordering = ['order']
|
||||||
|
|
||||||
project = models.ForeignKey(Project)
|
project = models.ForeignKey(Project, on_delete=models.CASCADE)
|
||||||
text = models.CharField(max_length=100)
|
text = models.CharField(max_length=100)
|
||||||
|
|
||||||
order = models.PositiveIntegerField(default=0, editable=False)
|
order = models.PositiveIntegerField(default=0, editable=False)
|
||||||
|
|
@ -91,7 +85,7 @@ class Note(SortableMixin):
|
||||||
# Registered as a tabular inline on `Project` which can't be sorted
|
# Registered as a tabular inline on `Project` which can't be sorted
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class NonSortableCredit(models.Model):
|
class NonSortableCredit(models.Model):
|
||||||
project = models.ForeignKey(Project)
|
project = models.ForeignKey(Project, on_delete=models.CASCADE)
|
||||||
first_name = models.CharField(max_length=30, help_text="Given name")
|
first_name = models.CharField(max_length=30, help_text="Given name")
|
||||||
last_name = models.CharField(max_length=30, help_text="Family name")
|
last_name = models.CharField(max_length=30, help_text="Family name")
|
||||||
|
|
||||||
|
|
@ -102,7 +96,7 @@ class NonSortableCredit(models.Model):
|
||||||
# Registered as a stacked inline on `Project` which can't be sorted
|
# Registered as a stacked inline on `Project` which can't be sorted
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class NonSortableNote(models.Model):
|
class NonSortableNote(models.Model):
|
||||||
project = models.ForeignKey(Project)
|
project = models.ForeignKey(Project, on_delete=models.CASCADE)
|
||||||
text = models.CharField(max_length=100)
|
text = models.CharField(max_length=100)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
@ -112,7 +106,7 @@ class NonSortableNote(models.Model):
|
||||||
# A generic bound model
|
# A generic bound model
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class GenericNote(SimpleModel, SortableMixin):
|
class GenericNote(SimpleModel, SortableMixin):
|
||||||
content_type = models.ForeignKey(ContentType,
|
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE,
|
||||||
verbose_name=u"Content type", related_name="generic_notes")
|
verbose_name=u"Content type", related_name="generic_notes")
|
||||||
object_id = models.PositiveIntegerField(u"Content id")
|
object_id = models.PositiveIntegerField(u"Content id")
|
||||||
content_object = GenericForeignKey(ct_field='content_type',
|
content_object = GenericForeignKey(ct_field='content_type',
|
||||||
|
|
@ -133,7 +127,7 @@ class Component(SimpleModel, SortableMixin):
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['order']
|
ordering = ['order']
|
||||||
|
|
||||||
widget = SortableForeignKey(Widget)
|
widget = SortableForeignKey(Widget, on_delete=models.CASCADE)
|
||||||
|
|
||||||
order = models.PositiveIntegerField(default=0, editable=False)
|
order = models.PositiveIntegerField(default=0, editable=False)
|
||||||
|
|
||||||
|
|
@ -183,7 +177,8 @@ class SortableCategoryWidget(SimpleModel, SortableMixin):
|
||||||
verbose_name = 'Sortable Category Widget'
|
verbose_name = 'Sortable Category Widget'
|
||||||
verbose_name_plural = 'Sortable Category Widgets'
|
verbose_name_plural = 'Sortable Category Widgets'
|
||||||
|
|
||||||
non_sortable_category = SortableForeignKey(NonSortableCategory)
|
non_sortable_category = SortableForeignKey(
|
||||||
|
NonSortableCategory, on_delete=models.CASCADE)
|
||||||
|
|
||||||
order = models.PositiveIntegerField(default=0, editable=False)
|
order = models.PositiveIntegerField(default=0, editable=False)
|
||||||
|
|
||||||
|
|
@ -197,7 +192,8 @@ class SortableNonInlineCategory(SimpleModel, SortableMixin):
|
||||||
that is *not* sortable, and is also not defined as an inline of the
|
that is *not* sortable, and is also not defined as an inline of the
|
||||||
SortableForeignKey field."""
|
SortableForeignKey field."""
|
||||||
|
|
||||||
non_sortable_category = SortableForeignKey(NonSortableCategory)
|
non_sortable_category = SortableForeignKey(
|
||||||
|
NonSortableCategory, on_delete=models.CASCADE)
|
||||||
|
|
||||||
order = models.PositiveIntegerField(default=0, editable=False)
|
order = models.PositiveIntegerField(default=0, editable=False)
|
||||||
|
|
||||||
|
|
@ -229,7 +225,7 @@ class CustomWidget(SortableMixin, SimpleModel):
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class CustomWidgetComponent(SortableMixin, SimpleModel):
|
class CustomWidgetComponent(SortableMixin, SimpleModel):
|
||||||
|
|
||||||
custom_widget = models.ForeignKey(CustomWidget)
|
custom_widget = models.ForeignKey(CustomWidget, on_delete=models.CASCADE)
|
||||||
|
|
||||||
# custom field for ordering
|
# custom field for ordering
|
||||||
widget_order = models.PositiveIntegerField(default=0, db_index=True,
|
widget_order = models.PositiveIntegerField(default=0, db_index=True,
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,13 @@
|
||||||
try:
|
try:
|
||||||
import httplib
|
import httplib # Python 2
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import http.client as httplib
|
import http.client as httplib # Python 3
|
||||||
|
|
||||||
from django import VERSION
|
|
||||||
|
|
||||||
if VERSION > (1, 8):
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
import django
|
||||||
|
|
||||||
from django import VERSION
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
@ -33,8 +30,7 @@ class TestSortableModel(SortableMixin):
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
|
|
||||||
if VERSION > (1, 8):
|
class TestNonAutoFieldModel(SortableMixin):
|
||||||
class TestNonAutoFieldModel(SortableMixin):
|
|
||||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||||
order = models.PositiveIntegerField(editable=False, db_index=True)
|
order = models.PositiveIntegerField(editable=False, db_index=True)
|
||||||
|
|
||||||
|
|
@ -79,8 +75,12 @@ class SortableTestCase(TestCase):
|
||||||
return category
|
return category
|
||||||
|
|
||||||
def test_new_user_is_authenticated(self):
|
def test_new_user_is_authenticated(self):
|
||||||
|
if django.VERSION < (1, 10):
|
||||||
self.assertEqual(self.user.is_authenticated(), True,
|
self.assertEqual(self.user.is_authenticated(), True,
|
||||||
'User is not authenticated')
|
'User is not authenticated')
|
||||||
|
else:
|
||||||
|
self.assertEqual(self.user.is_authenticated, True,
|
||||||
|
'User is not authenticated')
|
||||||
|
|
||||||
def test_new_user_is_staff(self):
|
def test_new_user_is_staff(self):
|
||||||
self.assertEqual(self.user.is_staff, True, 'User is not staff')
|
self.assertEqual(self.user.is_staff, True, 'User is not staff')
|
||||||
|
|
@ -114,7 +114,7 @@ class SortableTestCase(TestCase):
|
||||||
self.client.login(username=self.user.username,
|
self.client.login(username=self.user.username,
|
||||||
password=self.user_raw_password)
|
password=self.user_raw_password)
|
||||||
response = self.client.get('/admin/app/category/sort/')
|
response = self.client.get('/admin/app/category/sort/')
|
||||||
self.assertEquals(response.status_code, httplib.OK,
|
self.assertEqual(response.status_code, httplib.OK,
|
||||||
'Unable to reach sort view.')
|
'Unable to reach sort view.')
|
||||||
|
|
||||||
def make_test_categories(self):
|
def make_test_categories(self):
|
||||||
|
|
@ -257,7 +257,7 @@ class SortableTestCase(TestCase):
|
||||||
self.client.login(username=self.user.username,
|
self.client.login(username=self.user.username,
|
||||||
password=self.user_raw_password)
|
password=self.user_raw_password)
|
||||||
response = self.client.get('/admin/app/project/sort/')
|
response = self.client.get('/admin/app/project/sort/')
|
||||||
self.assertEquals(response.status_code, httplib.OK,
|
self.assertEqual(response.status_code, httplib.OK,
|
||||||
'Unable to reach sort view.')
|
'Unable to reach sort view.')
|
||||||
|
|
||||||
def test_adminsortable_change_list_view_permission_denied(self):
|
def test_adminsortable_change_list_view_permission_denied(self):
|
||||||
|
|
@ -267,7 +267,7 @@ class SortableTestCase(TestCase):
|
||||||
self.client.login(username=self.staff.username,
|
self.client.login(username=self.staff.username,
|
||||||
password=self.staff_raw_password)
|
password=self.staff_raw_password)
|
||||||
response = self.client.get('/admin/app/project/sort/')
|
response = self.client.get('/admin/app/project/sort/')
|
||||||
self.assertEquals(response.status_code, httplib.FORBIDDEN,
|
self.assertEqual(response.status_code, httplib.FORBIDDEN,
|
||||||
'Sort view must be forbidden.')
|
'Sort view must be forbidden.')
|
||||||
|
|
||||||
def test_adminsortable_inline_changelist_success(self):
|
def test_adminsortable_inline_changelist_success(self):
|
||||||
|
|
@ -317,8 +317,5 @@ class SortableTestCase(TestCase):
|
||||||
self.assertEqual(notes, expected_notes)
|
self.assertEqual(notes, expected_notes)
|
||||||
|
|
||||||
def test_save_non_auto_field_model(self):
|
def test_save_non_auto_field_model(self):
|
||||||
if VERSION > (1, 8):
|
|
||||||
model = TestNonAutoFieldModel()
|
model = TestNonAutoFieldModel()
|
||||||
model.save()
|
model.save()
|
||||||
else:
|
|
||||||
pass
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
# Django settings for test_project project.
|
# Django settings for test_project project.
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
import django
|
||||||
|
|
||||||
|
|
||||||
def map_path(directory_name):
|
def map_path(directory_name):
|
||||||
return os.path.join(os.path.dirname(__file__),
|
return os.path.join(os.path.dirname(__file__),
|
||||||
|
|
@ -8,7 +10,6 @@ def map_path(directory_name):
|
||||||
|
|
||||||
|
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
TEMPLATE_DEBUG = DEBUG
|
|
||||||
|
|
||||||
ADMINS = (
|
ADMINS = (
|
||||||
# ('Your Name', 'your_email@example.com'),
|
# ('Your Name', 'your_email@example.com'),
|
||||||
|
|
@ -91,35 +92,31 @@ STATICFILES_FINDERS = (
|
||||||
# Make this unique, and don't share it with anybody.
|
# Make this unique, and don't share it with anybody.
|
||||||
SECRET_KEY = '8**a!c8$1x)p@j2pj0yq!*v+dzp24g*$918ws#x@k+gf%0%rct'
|
SECRET_KEY = '8**a!c8$1x)p@j2pj0yq!*v+dzp24g*$918ws#x@k+gf%0%rct'
|
||||||
|
|
||||||
# List of callables that know how to import templates from various sources.
|
MIDDLEWARE = [
|
||||||
TEMPLATE_LOADERS = (
|
|
||||||
'django.template.loaders.filesystem.Loader',
|
|
||||||
'django.template.loaders.app_directories.Loader',
|
|
||||||
)
|
|
||||||
|
|
||||||
MIDDLEWARE_CLASSES = (
|
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
# Uncomment the next line for simple clickjacking protection:
|
# Uncomment the next line for simple clickjacking protection:
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
)
|
]
|
||||||
|
|
||||||
|
if django.VERSION < (1, 10):
|
||||||
|
MIDDLEWARE_CLASSES = MIDDLEWARE
|
||||||
|
|
||||||
ROOT_URLCONF = 'sample_project.urls'
|
ROOT_URLCONF = 'sample_project.urls'
|
||||||
|
|
||||||
# Python dotted path to the WSGI application used by Django's runserver.
|
# Python dotted path to the WSGI application used by Django's runserver.
|
||||||
WSGI_APPLICATION = 'sample_project.wsgi.application'
|
WSGI_APPLICATION = 'sample_project.wsgi.application'
|
||||||
|
|
||||||
TEMPLATE_DIRS = (
|
|
||||||
map_path('templates'),
|
|
||||||
)
|
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
'DIRS': TEMPLATE_DIRS,
|
'DIRS': [
|
||||||
|
map_path('templates')
|
||||||
|
],
|
||||||
'APP_DIRS': True,
|
'APP_DIRS': True,
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
'context_processors': [
|
'context_processors': [
|
||||||
|
|
|
||||||
|
|
@ -14,5 +14,5 @@ urlpatterns = [
|
||||||
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||||
|
|
||||||
# Uncomment the next line to enable the admin:
|
# Uncomment the next line to enable the admin:
|
||||||
url(r'^admin/', include(admin.site.urls)),
|
url(r'^admin/', admin.site.urls),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
import os
|
|
||||||
|
|
||||||
|
|
||||||
def map_path(directory_name):
|
|
||||||
return os.path.join(os.path.dirname(__file__),
|
|
||||||
'../' + directory_name).replace('\\', '/')
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
[tox]
|
||||||
|
envlist = django{1.8,1.9,1.10,1.11}-{py27,py34,py35},coverage
|
||||||
|
|
||||||
|
[testenv]
|
||||||
|
deps =
|
||||||
|
coverage
|
||||||
|
django1.8: Django>=1.8,<1.9
|
||||||
|
django1.9: Django>=1.9,<1.10
|
||||||
|
django1.10: Django>=1.10,<1.11
|
||||||
|
django1.11: Django>=1.11a1,<1.12
|
||||||
|
whitelist_externals = cd
|
||||||
|
setenv =
|
||||||
|
PYTHONPATH = {toxinidir}/sample_project
|
||||||
|
PYTHONWARNINGS = module
|
||||||
|
PYTHONDONTWRITEBYTECODE = 1
|
||||||
|
commands =
|
||||||
|
coverage run -p sample_project/manage.py test app
|
||||||
|
|
||||||
|
[testenv:coverage]
|
||||||
|
deps = coverage
|
||||||
|
skip_install = true
|
||||||
|
commands =
|
||||||
|
coverage combine
|
||||||
|
coverage report
|
||||||
|
coverage html
|
||||||
|
|
||||||
|
[coverage:run]
|
||||||
|
branch = True
|
||||||
|
parallel = True
|
||||||
|
source =
|
||||||
|
adminsortable
|
||||||
|
sample_project
|
||||||
|
|
||||||
|
[coverage:report]
|
||||||
|
exclude_lines =
|
||||||
|
if __name__ == .__main__.:
|
||||||
Loading…
Reference in New Issue