mirror of
https://github.com/Hopiu/django-select2.git
synced 2026-03-16 21:40:23 +00:00
Add Django 2.2 support
Add Django 2.2 LTS support Drop Django 1.11 LTS support Add request argument to `ModelSelect2Mixin.filter_queryset`
This commit is contained in:
parent
82859121a6
commit
9b54cc30c7
11 changed files with 83 additions and 102 deletions
11
.travis.yml
11
.travis.yml
|
|
@ -14,27 +14,26 @@ addons:
|
|||
- python-enchant
|
||||
- graphviz
|
||||
python:
|
||||
- "3.5"
|
||||
- "3.6"
|
||||
- "3.7"
|
||||
env:
|
||||
matrix:
|
||||
- DJANGO=111
|
||||
- DJANGO=20
|
||||
- DJANGO=21
|
||||
- DJANGO=22
|
||||
- DJANGO=master
|
||||
- TOXENV=qa
|
||||
- TOXENV=docs
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- env: DJANGO=master
|
||||
exclude:
|
||||
- python: "3.7"
|
||||
- python: "3.6"
|
||||
env: TOXENV=docs
|
||||
- python: "3.7"
|
||||
env: DJANGO=111
|
||||
- python: "3.6"
|
||||
env: TOXENV=qa
|
||||
env: TOXENV=docs
|
||||
|
||||
install:
|
||||
- pip install --upgrade pip tox
|
||||
- pip install -U codecov
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ from itertools import chain
|
|||
from pickle import PicklingError # nosec
|
||||
|
||||
from django import forms
|
||||
from django.contrib.admin.widgets import SELECT2_TRANSLATIONS
|
||||
from django.core import signing
|
||||
from django.db.models import Q
|
||||
from django.forms.models import ModelChoiceIterator
|
||||
|
|
@ -60,7 +61,7 @@ from .cache import cache
|
|||
from .conf import settings
|
||||
|
||||
|
||||
class Select2Mixin(object):
|
||||
class Select2Mixin:
|
||||
"""
|
||||
The base mixin of all Select2 widgets.
|
||||
|
||||
|
|
@ -99,22 +100,12 @@ class Select2Mixin(object):
|
|||
https://docs.djangoproject.com/en/stable/topics/forms/media/#media-as-a-dynamic-property
|
||||
"""
|
||||
lang = get_language()
|
||||
i18n_name = None
|
||||
select2_js = (settings.SELECT2_JS,) if settings.SELECT2_JS else ()
|
||||
select2_css = (settings.SELECT2_CSS,) if settings.SELECT2_CSS else ()
|
||||
|
||||
try:
|
||||
from django.contrib.admin.widgets import SELECT2_TRANSLATIONS
|
||||
i18n_name = SELECT2_TRANSLATIONS.get(lang)
|
||||
if i18n_name not in settings.SELECT2_I18N_AVAILABLE_LANGUAGES:
|
||||
i18n_name = None
|
||||
except ImportError:
|
||||
# TODO: select2 widget feature needs to be backported into Django 1.11
|
||||
try:
|
||||
i = [x.lower() for x in settings.SELECT2_I18N_AVAILABLE_LANGUAGES].index(lang)
|
||||
i18n_name = settings.SELECT2_I18N_AVAILABLE_LANGUAGES[i]
|
||||
except ValueError:
|
||||
pass
|
||||
i18n_name = SELECT2_TRANSLATIONS.get(lang)
|
||||
if i18n_name not in settings.SELECT2_I18N_AVAILABLE_LANGUAGES:
|
||||
i18n_name = None
|
||||
|
||||
i18n_file = ('%s/%s.js' % (settings.SELECT2_I18N_PATH, i18n_name),) if i18n_name else ()
|
||||
|
||||
|
|
@ -126,7 +117,7 @@ class Select2Mixin(object):
|
|||
media = property(_get_media)
|
||||
|
||||
|
||||
class Select2TagMixin(object):
|
||||
class Select2TagMixin:
|
||||
"""Mixin to add select2 tag functionality."""
|
||||
|
||||
def build_attrs(self, *args, **kwargs):
|
||||
|
|
@ -194,7 +185,7 @@ class Select2TagWidget(Select2TagMixin, Select2Mixin, forms.SelectMultiple):
|
|||
pass
|
||||
|
||||
|
||||
class HeavySelect2Mixin(object):
|
||||
class HeavySelect2Mixin:
|
||||
"""Mixin that adds select2's AJAX options and registers itself on Django's cache."""
|
||||
|
||||
dependent_fields = {}
|
||||
|
|
@ -317,7 +308,7 @@ class HeavySelect2TagWidget(HeavySelect2Mixin, Select2TagWidget):
|
|||
# Auto Heavy widgets
|
||||
|
||||
|
||||
class ModelSelect2Mixin(object):
|
||||
class ModelSelect2Mixin:
|
||||
"""Widget mixin that provides attributes and methods for :class:`.AutoResponseView`."""
|
||||
|
||||
model = None
|
||||
|
|
@ -352,7 +343,7 @@ class ModelSelect2Mixin(object):
|
|||
self.queryset = kwargs.pop('queryset', self.queryset)
|
||||
self.search_fields = kwargs.pop('search_fields', self.search_fields)
|
||||
self.max_results = kwargs.pop('max_results', self.max_results)
|
||||
defaults = {'data_view': 'django_select2-json'}
|
||||
defaults = {'data_view': 'django_select2:auto-json'}
|
||||
defaults.update(kwargs)
|
||||
super(ModelSelect2Mixin, self).__init__(*args, **defaults)
|
||||
|
||||
|
|
@ -370,13 +361,13 @@ class ModelSelect2Mixin(object):
|
|||
queryset.query,
|
||||
],
|
||||
'cls': self.__class__,
|
||||
'search_fields': self.search_fields,
|
||||
'max_results': self.max_results,
|
||||
'url': self.get_url(),
|
||||
'dependent_fields': self.dependent_fields,
|
||||
'search_fields': tuple(self.search_fields),
|
||||
'max_results': int(self.max_results),
|
||||
'url': str(self.get_url()),
|
||||
'dependent_fields': dict(self.dependent_fields),
|
||||
})
|
||||
|
||||
def filter_queryset(self, term, queryset=None, **dependent_fields):
|
||||
def filter_queryset(self, request, term, queryset=None, **dependent_fields):
|
||||
"""
|
||||
Return QuerySet filtered by search_fields matching the passed term.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,14 +3,18 @@ Django-Select2 URL configuration.
|
|||
|
||||
Add `django_select` to your ``urlconf`` **if** you use any 'Model' fields::
|
||||
|
||||
url(r'^select2/', include('django_select2.urls')),
|
||||
from django.urls import path
|
||||
|
||||
|
||||
path('select2/', include('django_select2.urls')),
|
||||
|
||||
"""
|
||||
from django.conf.urls import url
|
||||
from django.urls import path
|
||||
|
||||
from .views import AutoResponseView
|
||||
|
||||
app_name = 'django_select2'
|
||||
|
||||
urlpatterns = [
|
||||
url(r"^fields/auto.json$",
|
||||
AutoResponseView.as_view(), name="django_select2-json"),
|
||||
path("fields/auto.json", AutoResponseView.as_view(), name="auto-json"),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ class AutoResponseView(BaseListView):
|
|||
for form_field_name, model_field_name in self.widget.dependent_fields.items()
|
||||
if form_field_name in self.request.GET and self.request.GET.get(form_field_name, '') != ''
|
||||
}
|
||||
return self.widget.filter_queryset(self.term, self.queryset, **kwargs)
|
||||
return self.widget.filter_queryset(self.request, self.term, self.queryset, **kwargs)
|
||||
|
||||
def get_paginate_by(self, queryset):
|
||||
"""Paginate response by size of widget's `max_results` parameter."""
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ build-dir = docs/_build
|
|||
warning-is-error = 1
|
||||
|
||||
[tool:pytest]
|
||||
norecursedirs = env docs
|
||||
norecursedirs = env docs .eggs
|
||||
addopts = --tb=short -rxs
|
||||
DJANGO_SETTINGS_MODULE=tests.testapp.settings
|
||||
|
||||
|
|
|
|||
|
|
@ -22,10 +22,9 @@ def random_name(n):
|
|||
@pytest.yield_fixture(scope='session')
|
||||
def driver():
|
||||
chrome_options = webdriver.ChromeOptions()
|
||||
chrome_options.add_argument('headless')
|
||||
chrome_options.add_argument('window-size=1200x800')
|
||||
chrome_options.headless = True
|
||||
try:
|
||||
b = webdriver.Chrome(chrome_options=chrome_options)
|
||||
b = webdriver.Chrome(options=chrome_options)
|
||||
except WebDriverException as e:
|
||||
pytest.skip(force_text(e))
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import collections
|
||||
import json
|
||||
import os
|
||||
from collections.abc import Iterable
|
||||
|
||||
import pytest
|
||||
from django.core import signing
|
||||
from django.db.models import QuerySet
|
||||
from django.urls import reverse
|
||||
from django.utils import translation
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.six import text_type
|
||||
from selenium.common.exceptions import NoSuchElementException
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support import expected_conditions
|
||||
|
|
@ -25,13 +25,8 @@ from tests.testapp.forms import (
|
|||
)
|
||||
from tests.testapp.models import Artist, City, Country, Genre, Groupie
|
||||
|
||||
try:
|
||||
from django.urls import reverse
|
||||
except ImportError:
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
|
||||
class TestSelect2Mixin(object):
|
||||
class TestSelect2Mixin:
|
||||
url = reverse('select2_widget')
|
||||
form = forms.AlbumSelect2WidgetForm()
|
||||
multiple_form = forms.AlbumSelect2MultipleWidgetForm()
|
||||
|
|
@ -40,7 +35,7 @@ class TestSelect2Mixin(object):
|
|||
def test_initial_data(self, genres):
|
||||
genre = genres[0]
|
||||
form = self.form.__class__(initial={'primary_genre': genre.pk})
|
||||
assert text_type(genre) in form.as_p()
|
||||
assert str(genre) in form.as_p()
|
||||
|
||||
def test_initial_form_class(self):
|
||||
widget = self.widget_cls(attrs={'class': 'my-class'})
|
||||
|
|
@ -147,7 +142,7 @@ class TestSelect2Mixin(object):
|
|||
)
|
||||
|
||||
|
||||
class TestSelect2MixinSettings(object):
|
||||
class TestSelect2MixinSettings:
|
||||
def test_default_media(self):
|
||||
sut = Select2Widget()
|
||||
result = sut.media.render()
|
||||
|
|
@ -240,12 +235,12 @@ class TestHeavySelect2Mixin(TestSelect2Mixin):
|
|||
|
||||
def test_get_url(self):
|
||||
widget = self.widget_cls(data_view='heavy_data_1', attrs={'class': 'my-class'})
|
||||
assert isinstance(widget.get_url(), text_type)
|
||||
assert isinstance(widget.get_url(), str)
|
||||
|
||||
def test_can_not_pickle(self):
|
||||
widget = self.widget_cls(data_view='heavy_data_1', attrs={'class': 'my-class'})
|
||||
|
||||
class NoPickle(object):
|
||||
class NoPickle:
|
||||
pass
|
||||
|
||||
widget.no_pickle = NoPickle()
|
||||
|
|
@ -260,7 +255,7 @@ class TestModelSelect2Mixin(TestHeavySelect2Mixin):
|
|||
def test_initial_data(self, genres):
|
||||
genre = genres[0]
|
||||
form = self.form.__class__(initial={'primary_genre': genre.pk})
|
||||
assert text_type(genre) in form.as_p()
|
||||
assert str(genre) in form.as_p()
|
||||
|
||||
def test_label_from_instance_initial(self, genres):
|
||||
genre = genres[0]
|
||||
|
|
@ -326,34 +321,34 @@ class TestModelSelect2Mixin(TestHeavySelect2Mixin):
|
|||
widget.get_search_fields()
|
||||
|
||||
widget.search_fields = ['title__icontains']
|
||||
assert isinstance(widget.get_search_fields(), collections.Iterable)
|
||||
assert all(isinstance(x, text_type) for x in widget.get_search_fields())
|
||||
assert isinstance(widget.get_search_fields(), Iterable)
|
||||
assert all(isinstance(x, str) for x in widget.get_search_fields())
|
||||
|
||||
def test_filter_queryset(self, genres):
|
||||
widget = TitleModelSelect2Widget(queryset=Genre.objects.all())
|
||||
assert widget.filter_queryset(genres[0].title[:3]).exists()
|
||||
assert widget.filter_queryset(None, genres[0].title[:3]).exists()
|
||||
|
||||
widget = TitleModelSelect2Widget(search_fields=['title__icontains'],
|
||||
queryset=Genre.objects.all())
|
||||
qs = widget.filter_queryset(" ".join([genres[0].title[:3], genres[0].title[3:]]))
|
||||
qs = widget.filter_queryset(None, " ".join([genres[0].title[:3], genres[0].title[3:]]))
|
||||
assert qs.exists()
|
||||
|
||||
def test_model_kwarg(self):
|
||||
widget = ModelSelect2Widget(model=Genre, search_fields=['title__icontains'])
|
||||
genre = Genre.objects.last()
|
||||
result = widget.filter_queryset(genre.title)
|
||||
result = widget.filter_queryset(None, genre.title)
|
||||
assert result.exists()
|
||||
|
||||
def test_queryset_kwarg(self):
|
||||
widget = ModelSelect2Widget(queryset=Genre.objects.all(), search_fields=['title__icontains'])
|
||||
genre = Genre.objects.last()
|
||||
result = widget.filter_queryset(genre.title)
|
||||
result = widget.filter_queryset(None, genre.title)
|
||||
assert result.exists()
|
||||
|
||||
def test_ajax_view_registration(self, client):
|
||||
widget = ModelSelect2Widget(queryset=Genre.objects.all(), search_fields=['title__icontains'])
|
||||
widget.render('name', 'value')
|
||||
url = reverse('django_select2-json')
|
||||
url = reverse('django_select2:auto-json')
|
||||
genre = Genre.objects.last()
|
||||
response = client.get(url, data=dict(field_id=signing.dumps(id(widget)), term=genre.title))
|
||||
assert response.status_code == 200, response.content
|
||||
|
|
@ -366,14 +361,14 @@ class TestModelSelect2Mixin(TestHeavySelect2Mixin):
|
|||
widget.render('name', 'value')
|
||||
cached_widget = cache.get(widget._get_cache_key())
|
||||
assert cached_widget['max_results'] == widget.max_results
|
||||
assert cached_widget['search_fields'] == widget.search_fields
|
||||
assert cached_widget['search_fields'] == tuple(widget.search_fields)
|
||||
qs = widget.get_queryset()
|
||||
assert isinstance(cached_widget['queryset'][0], qs.__class__)
|
||||
assert text_type(cached_widget['queryset'][1]) == text_type(qs.query)
|
||||
assert str(cached_widget['queryset'][1]) == str(qs.query)
|
||||
|
||||
def test_get_url(self):
|
||||
widget = ModelSelect2Widget(queryset=Genre.objects.all(), search_fields=['title__icontains'])
|
||||
assert isinstance(widget.get_url(), text_type)
|
||||
assert isinstance(widget.get_url(), str)
|
||||
|
||||
def test_custom_to_field_name(self):
|
||||
the_best_band_in_the_world = Artist.objects.create(title='Take That')
|
||||
|
|
@ -398,7 +393,7 @@ class TestHeavySelect2TagWidget(TestHeavySelect2Mixin):
|
|||
assert 'data-minimum-input-length="3"' in output
|
||||
|
||||
|
||||
class TestHeavySelect2MultipleWidget(object):
|
||||
class TestHeavySelect2MultipleWidget:
|
||||
url = reverse('heavy_select2_multiple_widget')
|
||||
form = forms.HeavySelect2MultipleWidgetForm()
|
||||
widget_cls = HeavySelect2MultipleWidget
|
||||
|
|
@ -428,7 +423,7 @@ class TestHeavySelect2MultipleWidget(object):
|
|||
assert result_title == 'One'
|
||||
|
||||
|
||||
class TestAddressChainedSelect2Widget(object):
|
||||
class TestAddressChainedSelect2Widget:
|
||||
url = reverse('model_chained_select2_widget')
|
||||
form = forms.AddressChainedSelect2WidgetForm()
|
||||
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@ except ImportError:
|
|||
from django.core.urlresolvers import reverse
|
||||
|
||||
|
||||
class TestAutoResponseView(object):
|
||||
class TestAutoResponseView:
|
||||
def test_get(self, client, artists):
|
||||
artist = artists[0]
|
||||
form = AlbumModelSelect2WidgetForm()
|
||||
assert form.as_p()
|
||||
field_id = signing.dumps(id(form.fields['artist'].widget))
|
||||
url = reverse('django_select2-json')
|
||||
url = reverse('django_select2:auto-json')
|
||||
response = client.get(url, {'field_id': field_id, 'term': artist.title})
|
||||
assert response.status_code == 200
|
||||
data = json.loads(response.content.decode('utf-8'))
|
||||
|
|
@ -31,25 +31,25 @@ class TestAutoResponseView(object):
|
|||
|
||||
def test_no_field_id(self, client, artists):
|
||||
artist = artists[0]
|
||||
url = reverse('django_select2-json')
|
||||
url = reverse('django_select2:auto-json')
|
||||
response = client.get(url, {'term': artist.title})
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_wrong_field_id(self, client, artists):
|
||||
artist = artists[0]
|
||||
url = reverse('django_select2-json')
|
||||
url = reverse('django_select2:auto-json')
|
||||
response = client.get(url, {'field_id': 123, 'term': artist.title})
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_field_id_not_found(self, client, artists):
|
||||
artist = artists[0]
|
||||
field_id = signing.dumps(123456789)
|
||||
url = reverse('django_select2-json')
|
||||
url = reverse('django_select2:auto-json')
|
||||
response = client.get(url, {'field_id': field_id, 'term': artist.title})
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_pagination(self, genres, client):
|
||||
url = reverse('django_select2-json')
|
||||
url = reverse('django_select2:auto-json')
|
||||
widget = ModelSelect2Widget(
|
||||
max_results=10,
|
||||
model=Genre,
|
||||
|
|
@ -72,7 +72,7 @@ class TestAutoResponseView(object):
|
|||
assert data['more'] is False
|
||||
|
||||
def test_label_from_instance(self, artists, client):
|
||||
url = reverse('django_select2-json')
|
||||
url = reverse('django_select2:auto-json')
|
||||
|
||||
form = AlbumModelSelect2WidgetForm()
|
||||
form.fields['artist'].widget = ArtistCustomTitleWidget()
|
||||
|
|
@ -96,6 +96,6 @@ class TestAutoResponseView(object):
|
|||
widget_dict = cache.get(cache_key)
|
||||
widget_dict['url'] = 'yet/another/url'
|
||||
cache.set(cache_key, widget_dict)
|
||||
url = reverse('django_select2-json')
|
||||
url = reverse('django_select2:auto-json')
|
||||
response = client.get(url, {'field_id': field_id, 'term': artist.title})
|
||||
assert response.status_code == 404
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from tests.testapp import models
|
|||
from tests.testapp.models import Album, City, Country
|
||||
|
||||
|
||||
class TitleSearchFieldMixin(object):
|
||||
class TitleSearchFieldMixin:
|
||||
search_fields = [
|
||||
'title__icontains',
|
||||
'pk__startswith'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from django.conf.urls import include, url
|
||||
from django.urls import include, path
|
||||
|
||||
from .forms import (
|
||||
AddressChainedSelect2WidgetForm, AlbumModelSelect2WidgetForm,
|
||||
|
|
@ -8,28 +8,28 @@ from .forms import (
|
|||
from .views import TemplateFormView, heavy_data_1, heavy_data_2
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^select2_widget/$',
|
||||
TemplateFormView.as_view(form_class=Select2WidgetForm), name='select2_widget'),
|
||||
url(r'^heavy_select2_widget/$',
|
||||
TemplateFormView.as_view(form_class=HeavySelect2WidgetForm), name='heavy_select2_widget'),
|
||||
url(r'^heavy_select2_multiple_widget/$',
|
||||
TemplateFormView.as_view(form_class=HeavySelect2MultipleWidgetForm, success_url='/'),
|
||||
name='heavy_select2_multiple_widget'),
|
||||
path('select2_widget',
|
||||
TemplateFormView.as_view(form_class=Select2WidgetForm), name='select2_widget'),
|
||||
path('heavy_select2_widget',
|
||||
TemplateFormView.as_view(form_class=HeavySelect2WidgetForm), name='heavy_select2_widget'),
|
||||
path('heavy_select2_multiple_widget',
|
||||
TemplateFormView.as_view(form_class=HeavySelect2MultipleWidgetForm, success_url='/'),
|
||||
name='heavy_select2_multiple_widget'),
|
||||
|
||||
url(r'^model_select2_widget/$',
|
||||
TemplateFormView.as_view(form_class=AlbumModelSelect2WidgetForm),
|
||||
name='model_select2_widget'),
|
||||
path('model_select2_widget',
|
||||
TemplateFormView.as_view(form_class=AlbumModelSelect2WidgetForm),
|
||||
name='model_select2_widget'),
|
||||
|
||||
url(r'^model_select2_tag_widget/$',
|
||||
TemplateFormView.as_view(form_class=ModelSelect2TagWidgetForm),
|
||||
name='model_select2_tag_widget'),
|
||||
path('model_select2_tag_widget',
|
||||
TemplateFormView.as_view(form_class=ModelSelect2TagWidgetForm),
|
||||
name='model_select2_tag_widget'),
|
||||
|
||||
url(r'^model_chained_select2_widget/$',
|
||||
TemplateFormView.as_view(form_class=AddressChainedSelect2WidgetForm),
|
||||
name='model_chained_select2_widget'),
|
||||
path('model_chained_select2_widget',
|
||||
TemplateFormView.as_view(form_class=AddressChainedSelect2WidgetForm),
|
||||
name='model_chained_select2_widget'),
|
||||
|
||||
url(r'^heavy_data_1/$', heavy_data_1, name='heavy_data_1'),
|
||||
url(r'^heavy_data_2/$', heavy_data_2, name='heavy_data_2'),
|
||||
path('heavy_data_1', heavy_data_1, name='heavy_data_1'),
|
||||
path('heavy_data_2', heavy_data_2, name='heavy_data_2'),
|
||||
|
||||
url(r'^select2/', include('django_select2.urls')),
|
||||
path('select2/', include('django_select2.urls')),
|
||||
]
|
||||
|
|
|
|||
13
tox.ini
13
tox.ini
|
|
@ -1,14 +1,14 @@
|
|||
[tox]
|
||||
envlist = py{36}-dj{111,20,21,master},qa,docs
|
||||
envlist = py{35,36,37}-dj{20,21,22,master},docs
|
||||
[testenv]
|
||||
setenv=
|
||||
PYTHONPATH = {toxinidir}
|
||||
passenv=CI
|
||||
deps=
|
||||
-rrequirements-dev.txt
|
||||
dj111: https://github.com/django/django/archive/stable/1.11.x.tar.gz#egg=django
|
||||
dj20: https://github.com/django/django/archive/stable/2.0.x.tar.gz#egg=django
|
||||
dj21: https://github.com/django/django/archive/stable/2.1.x.tar.gz#egg=django
|
||||
dj22: https://github.com/django/django/archive/stable/2.2.x.tar.gz#egg=django
|
||||
djmaster: https://github.com/django/django/archive/master.tar.gz#egg=django
|
||||
commands=
|
||||
coverage run --source=django_select2 -m 'pytest' \
|
||||
|
|
@ -16,15 +16,8 @@ commands=
|
|||
--ignore=.tox \
|
||||
{posargs}
|
||||
|
||||
[testenv:qa]
|
||||
changedir={toxinidir}
|
||||
deps=
|
||||
-rrequirements-dev.txt
|
||||
commands=
|
||||
isort --check-only --recursive --diff {posargs}
|
||||
|
||||
[testenv:docs]
|
||||
deps=
|
||||
-rrequirements-dev.txt
|
||||
https://github.com/django/django/archive/stable/1.11.x.tar.gz#egg=django
|
||||
https://github.com/django/django/archive/stable/2.2.x.tar.gz#egg=django
|
||||
commands=python setup.py build_sphinx
|
||||
|
|
|
|||
Loading…
Reference in a new issue