mirror of
https://github.com/Hopiu/django-select2.git
synced 2026-05-25 13:03:43 +00:00
Added tests
This commit is contained in:
parent
f80abb10f2
commit
5dfd8553cc
15 changed files with 413 additions and 343 deletions
|
|
@ -14,23 +14,19 @@ env:
|
||||||
global:
|
global:
|
||||||
- DISPLAY=:99.0
|
- DISPLAY=:99.0
|
||||||
matrix:
|
matrix:
|
||||||
- DJANGO="Django<1.7,>=1.6"
|
|
||||||
- DJANGO="Django<1.8,>=1.7"
|
- DJANGO="Django<1.8,>=1.7"
|
||||||
- DJANGO="Django<1.9,>=1.8"
|
- DJANGO="Django<1.9,>=1.8"
|
||||||
- DJANGO="-e git+https://github.com/django/django.git@master#egg=Django"
|
- DJANGO="-e git+https://github.com/django/django.git@master#egg=Django"
|
||||||
matrix:
|
matrix:
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- env: DJANGO="Django<1.7,>=1.6"
|
|
||||||
- env: DJANGO="-e git+https://github.com/django/django.git@master#egg=Django"
|
- env: DJANGO="-e git+https://github.com/django/django.git@master#egg=Django"
|
||||||
- python: "pypy"
|
|
||||||
- python: "pypy3"
|
|
||||||
install:
|
install:
|
||||||
- pip install --upgrade pip
|
- pip install --upgrade pip
|
||||||
- pip install -e .
|
- pip install -e .
|
||||||
- pip install -r requirements_dev.txt
|
- pip install -r requirements_dev.txt
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == 2* ]]; then pip install python-memcached; fi
|
- if [[ $TRAVIS_PYTHON_VERSION == 2* ]] || [[ $TRAVIS_PYTHON_VERSION == pypy ]]; then pip install python-memcached; fi
|
||||||
- if [[ $TRAVIS_PYTHON_VERSION == 3* ]]; then pip install python3-memcached; fi
|
- if [[ $TRAVIS_PYTHON_VERSION == 3* ]] || [[ $TRAVIS_PYTHON_VERSION == pypy3 ]]; then pip install python3-memcached; fi
|
||||||
- pip install $DJANGO
|
- pip install $DJANGO
|
||||||
- pip install coveralls
|
- pip install coveralls
|
||||||
- sh -e /etc/init.d/xvfb start
|
- sh -e /etc/init.d/xvfb start
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ Light widgets are normally named, i.e. there is no
|
||||||
:py:class:`.ModelSelect2Widget`,
|
:py:class:`.ModelSelect2Widget`,
|
||||||
:py:class:`.ModelSelect2MultipleWidget`,
|
:py:class:`.ModelSelect2MultipleWidget`,
|
||||||
:py:class:`.HeavySelect2TagWidget`,
|
:py:class:`.HeavySelect2TagWidget`,
|
||||||
:py:class:`.ModelSelect2TagWidget`
|
:py:class:`.GenreSelect2TagWidget`
|
||||||
|
|
||||||
`Read more`_
|
`Read more`_
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,14 +32,14 @@ class Select2Mixin(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def build_attrs(self, extra_attrs=None, **kwargs):
|
def build_attrs(self, extra_attrs=None, **kwargs):
|
||||||
attrs = super(Select2Mixin, self).build_attrs(extra_attrs=None, **kwargs)
|
attrs = super(Select2Mixin, self).build_attrs(extra_attrs=extra_attrs, **kwargs)
|
||||||
if self.is_required:
|
if self.is_required:
|
||||||
attrs.setdefault('data-allow-clear', 'false')
|
attrs.setdefault('data-allow-clear', 'false')
|
||||||
else:
|
else:
|
||||||
attrs.setdefault('data-allow-clear', 'true')
|
attrs.setdefault('data-allow-clear', 'true')
|
||||||
attrs.setdefault('data-placeholder', '')
|
attrs.setdefault('data-placeholder', '')
|
||||||
|
|
||||||
attrs.setdefault('data-minimumInputLength', 0)
|
attrs.setdefault('data-minimum-input-length', 0)
|
||||||
if 'class' in attrs:
|
if 'class' in attrs:
|
||||||
attrs['class'] += ' django-select2'
|
attrs['class'] += ' django-select2'
|
||||||
else:
|
else:
|
||||||
|
|
@ -47,7 +47,7 @@ class Select2Mixin(object):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
def render_options(self, choices, selected_choices):
|
def render_options(self, choices, selected_choices):
|
||||||
output = '<option></option>\n' if not self.is_required else ''
|
output = '<option></option>' if not self.is_required else ''
|
||||||
output += super(Select2Mixin, self).render_options(choices, selected_choices)
|
output += super(Select2Mixin, self).render_options(choices, selected_choices)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
@ -86,7 +86,7 @@ class HeavySelect2Mixin(Select2Mixin):
|
||||||
self.data_view = kwargs.pop('data_view', None)
|
self.data_view = kwargs.pop('data_view', None)
|
||||||
self.data_url = kwargs.pop('data_url', None)
|
self.data_url = kwargs.pop('data_url', None)
|
||||||
if not (self.data_view or self.data_url):
|
if not (self.data_view or self.data_url):
|
||||||
ValueError('You must ether specify "data_view" or "data_url".')
|
raise ValueError('You must ether specify "data_view" or "data_url".')
|
||||||
self.userGetValTextFuncName = kwargs.pop('userGetValTextFuncName', 'null')
|
self.userGetValTextFuncName = kwargs.pop('userGetValTextFuncName', 'null')
|
||||||
super(HeavySelect2Mixin, self).__init__(**kwargs)
|
super(HeavySelect2Mixin, self).__init__(**kwargs)
|
||||||
|
|
||||||
|
|
@ -96,39 +96,37 @@ class HeavySelect2Mixin(Select2Mixin):
|
||||||
return reverse_lazy(self.data_view)
|
return reverse_lazy(self.data_view)
|
||||||
|
|
||||||
def build_attrs(self, extra_attrs=None, **kwargs):
|
def build_attrs(self, extra_attrs=None, **kwargs):
|
||||||
attrs = super(HeavySelect2Mixin, self).build_attrs(extra_attrs, **kwargs)
|
attrs = super(HeavySelect2Mixin, self).build_attrs(extra_attrs=extra_attrs, **kwargs)
|
||||||
|
|
||||||
# encrypt instance Id
|
# encrypt instance Id
|
||||||
widget_id = signing.dumps(id(self))
|
self.widget_id = signing.dumps(id(self))
|
||||||
# add widget object to cache
|
|
||||||
cache.set(self._get_cache_key(), self)
|
|
||||||
|
|
||||||
attrs['data-field_id'] = widget_id
|
attrs['data-field_id'] = self.widget_id
|
||||||
attrs.setdefault('data-ajax--url', self.get_url())
|
attrs.setdefault('data-ajax--url', self.get_url())
|
||||||
attrs.setdefault('data-ajax--cache', "true")
|
attrs.setdefault('data-ajax--cache', "true")
|
||||||
attrs.setdefault('data-ajax--type', "GET")
|
attrs.setdefault('data-ajax--type', "GET")
|
||||||
attrs.setdefault('data-minimumInputLength', 2)
|
attrs.setdefault('data-minimum-input-length', 2)
|
||||||
|
|
||||||
if 'class' in attrs:
|
attrs['class'] += ' django-select2-heavy'
|
||||||
attrs['class'] += ' django-select2-heavy'
|
|
||||||
else:
|
|
||||||
attrs['class'] = 'django-select2-heavy'
|
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
def render(self, name, value, attrs=None, choices=()):
|
||||||
|
output = super(HeavySelect2Mixin, self).render(name, value, attrs=attrs, choices=choices)
|
||||||
|
self.set_to_cache()
|
||||||
|
return output
|
||||||
|
|
||||||
def _get_cache_key(self):
|
def _get_cache_key(self):
|
||||||
return "%s%s" % (settings.SELECT2_CACHE_PREFIX, id(self))
|
return "%s%s" % (settings.SELECT2_CACHE_PREFIX, id(self))
|
||||||
|
|
||||||
def value_from_datadict(self, *args, **kwargs):
|
def set_to_cache(self):
|
||||||
return super(HeavySelect2Mixin, self).value_from_datadict(*args, **kwargs)
|
"""Add widget object to Djnago's cache."""
|
||||||
|
cache.set(self._get_cache_key(), self)
|
||||||
|
|
||||||
def render_options(self, choices, selected_choices):
|
def render_options(self, choices, selected_choices):
|
||||||
output = [super(HeavySelect2Mixin, self).render_options(choices, selected_choices)]
|
output = ['<option></option>' if not self.is_required else '']
|
||||||
if isinstance(choices, ModelChoiceIterator):
|
choices = {(k, v) for k, v in choices if k in selected_choices}
|
||||||
selected_choices = set(choices.choice(obj)
|
selected_choices = {force_text(v) for v in selected_choices}
|
||||||
for obj in choices.queryset.filter(pk__in=selected_choices))
|
for option_value, option_label in choices:
|
||||||
else:
|
|
||||||
selected_choices = set((force_text(v), v) for v in choices if v in selected_choices)
|
|
||||||
for option_label, option_value in selected_choices:
|
|
||||||
output.append(self.render_option(selected_choices, option_value, option_label))
|
output.append(self.render_option(selected_choices, option_value, option_label))
|
||||||
return '\n'.join(output)
|
return '\n'.join(output)
|
||||||
|
|
||||||
|
|
@ -143,10 +141,10 @@ class HeavySelect2MultipleWidget(HeavySelect2Mixin, forms.SelectMultiple):
|
||||||
|
|
||||||
class HeavySelect2TagWidget(HeavySelect2MultipleWidget):
|
class HeavySelect2TagWidget(HeavySelect2MultipleWidget):
|
||||||
def build_attrs(self, extra_attrs=None, **kwargs):
|
def build_attrs(self, extra_attrs=None, **kwargs):
|
||||||
attrs = super(HeavySelect2TagWidget, self).build_attrs(self, extra_attrs, **kwargs)
|
attrs = super(HeavySelect2TagWidget, self).build_attrs(extra_attrs, **kwargs)
|
||||||
attrs['data-minimumInputLength'] = 1
|
attrs['data-minimum-input-length'] = 1
|
||||||
attrs['data-tags'] = 'true'
|
attrs['data-tags'] = 'true'
|
||||||
attrs['data-tokenSeparators'] = [",", " "]
|
attrs['data-token-separators'] = [",", " "]
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -154,9 +152,6 @@ class HeavySelect2TagWidget(HeavySelect2MultipleWidget):
|
||||||
|
|
||||||
|
|
||||||
class ModelSelect2Mixin(object):
|
class ModelSelect2Mixin(object):
|
||||||
|
|
||||||
"""Mixin for """
|
|
||||||
|
|
||||||
model = None
|
model = None
|
||||||
queryset = None
|
queryset = None
|
||||||
search_fields = []
|
search_fields = []
|
||||||
|
|
@ -198,6 +193,22 @@ class ModelSelect2Mixin(object):
|
||||||
return self.search_fields
|
return self.search_fields
|
||||||
raise NotImplementedError('%s, must implement "search_fields".' % self.__class__.__name__)
|
raise NotImplementedError('%s, must implement "search_fields".' % self.__class__.__name__)
|
||||||
|
|
||||||
|
def render_options(self, choices, selected_choices):
|
||||||
|
output = ['<option></option>' if not self.is_required else '']
|
||||||
|
if isinstance(self.choices, ModelChoiceIterator):
|
||||||
|
if not self.queryset:
|
||||||
|
self.queryset = self.choices.queryset
|
||||||
|
selected_choices = {c for c in selected_choices
|
||||||
|
if c not in self.choices.field.empty_values}
|
||||||
|
choices = {self.choices.choice(obj)
|
||||||
|
for obj in self.choices.queryset.filter(pk__in=selected_choices)}
|
||||||
|
else:
|
||||||
|
choices = {(k, v) for k, v in choices if k in selected_choices}
|
||||||
|
selected_choices = {force_text(v) for v in selected_choices}
|
||||||
|
for option_value, option_label in choices:
|
||||||
|
output.append(self.render_option(selected_choices, option_value, option_label))
|
||||||
|
return '\n'.join(output)
|
||||||
|
|
||||||
|
|
||||||
class ModelSelect2Widget(ModelSelect2Mixin, HeavySelect2Widget):
|
class ModelSelect2Widget(ModelSelect2Mixin, HeavySelect2Widget):
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
[pytest]
|
[pytest]
|
||||||
|
norecursedirs = env
|
||||||
addopts = --tb=short -rxs
|
addopts = --tb=short -rxs
|
||||||
DJANGO_SETTINGS_MODULE=tests.testapp.settings
|
DJANGO_SETTINGS_MODULE=tests.testapp.settings
|
||||||
|
|
||||||
|
|
|
||||||
48
setup.py
48
setup.py
|
|
@ -23,50 +23,6 @@ URL = "https://github.com/applegrew/django-select2"
|
||||||
VERSION = __import__(PACKAGE).__version__
|
VERSION = __import__(PACKAGE).__version__
|
||||||
|
|
||||||
|
|
||||||
def getPkgPath():
|
|
||||||
return __import__(PACKAGE).__path__[0] + '/'
|
|
||||||
|
|
||||||
|
|
||||||
def minify(files, outfile, ftype):
|
|
||||||
import requests
|
|
||||||
import io
|
|
||||||
|
|
||||||
content = ''
|
|
||||||
|
|
||||||
for filename in files:
|
|
||||||
with io.open(getPkgPath() + filename, 'r', encoding='utf8') as f:
|
|
||||||
content = content + '\n' + f.read()
|
|
||||||
|
|
||||||
data = {
|
|
||||||
'code': content,
|
|
||||||
'type': ftype,
|
|
||||||
}
|
|
||||||
response = requests.post('http://api.applegrew.com/minify', data)
|
|
||||||
response.raise_for_status()
|
|
||||||
response = response.json()
|
|
||||||
if response['success']:
|
|
||||||
with io.open(getPkgPath() + outfile, 'w', encoding='utf8') as f:
|
|
||||||
f.write(response['compiled_code'])
|
|
||||||
else:
|
|
||||||
raise Exception('%(error_code)s: "%(error)s"' % response)
|
|
||||||
|
|
||||||
|
|
||||||
if len(sys.argv) > 1 and 'sdist' == sys.argv[1]:
|
|
||||||
minify(['static/django_select2/js/select2.js'], 'static/django_select2/js/select2.min.js', 'js')
|
|
||||||
minify(['static/django_select2/js/heavy_data.js'], 'static/django_select2/js/heavy_data.min.js', 'js')
|
|
||||||
minify(['static/django_select2/css/select2.css'], 'static/django_select2/css/select2.min.css', 'css')
|
|
||||||
minify(['static/django_select2/css/select2.css', 'static/django_select2/css/extra.css'],
|
|
||||||
'static/django_select2/css/all.min.css', 'css')
|
|
||||||
minify(['static/django_select2/css/select2.css', 'static/django_select2/css/select2-bootstrap.css'],
|
|
||||||
'static/django_select2/css/select2-bootstrapped.min.css', 'css')
|
|
||||||
minify(
|
|
||||||
[
|
|
||||||
'static/django_select2/css/select2.css',
|
|
||||||
'static/django_select2/css/extra.css',
|
|
||||||
'static/django_select2/css/select2-bootstrap.css'
|
|
||||||
], 'static/django_select2/css/all-bootstrapped.min.css', 'css')
|
|
||||||
|
|
||||||
|
|
||||||
class PyTest(Command):
|
class PyTest(Command):
|
||||||
user_options = []
|
user_options = []
|
||||||
|
|
||||||
|
|
@ -102,6 +58,10 @@ setup(
|
||||||
"License :: OSI Approved :: Apache Software License",
|
"License :: OSI Approved :: Apache Software License",
|
||||||
"Operating System :: OS Independent",
|
"Operating System :: OS Independent",
|
||||||
"Programming Language :: Python",
|
"Programming Language :: Python",
|
||||||
|
"Programming Language :: Python :: 2",
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"Framework :: Django :: 1.7",
|
||||||
|
"Framework :: Django :: 1.8",
|
||||||
"Framework :: Django",
|
"Framework :: Django",
|
||||||
],
|
],
|
||||||
install_requires=[
|
install_requires=[
|
||||||
|
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
from __future__ import print_function, unicode_literals
|
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from django.utils.encoding import smart_text
|
|
||||||
from selenium.common.exceptions import NoSuchElementException
|
|
||||||
|
|
||||||
from django_select2.types import NO_ERR_RESP
|
|
||||||
from tests.testapp.forms import AlbumForm, ArtistForm
|
|
||||||
|
|
||||||
|
|
||||||
class ViewTestMixin(object):
|
|
||||||
url = ''
|
|
||||||
|
|
||||||
def test_get(self, client):
|
|
||||||
response = client.get(self.url)
|
|
||||||
assert response.status_code == 200
|
|
||||||
|
|
||||||
|
|
||||||
class TestAutoModelSelect2TagField(object):
|
|
||||||
url = reverse('single_value_model_field')
|
|
||||||
|
|
||||||
def test_no_js_error(self, db, client, live_server, driver, genres):
|
|
||||||
driver.get(live_server + self.url)
|
|
||||||
with pytest.raises(NoSuchElementException):
|
|
||||||
error = driver.find_element_by_xpath('//body[@JSError]')
|
|
||||||
pytest.fail(error.get_attribute('JSError'))
|
|
||||||
|
|
||||||
def test_form(self):
|
|
||||||
form = ArtistForm()
|
|
||||||
assert form
|
|
||||||
|
|
||||||
|
|
||||||
class TestAutoModelSelect2Field(object):
|
|
||||||
def test_form(self, client, artists):
|
|
||||||
artist = artists[0]
|
|
||||||
form = AlbumForm()
|
|
||||||
assert form.as_p()
|
|
||||||
field_id = form.fields['artist'].widget.widget_id
|
|
||||||
url = reverse('django_select2_central_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'))
|
|
||||||
assert data['results']
|
|
||||||
assert {'id': artist.pk, 'text': smart_text(artist)} in data['results']
|
|
||||||
assert data['more'] is False
|
|
||||||
assert data['err'] == NO_ERR_RESP
|
|
||||||
188
tests/test_forms.py
Normal file
188
tests/test_forms.py
Normal file
|
|
@ -0,0 +1,188 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
import collections
|
||||||
|
import json
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from django.core import signing
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.db.models import QuerySet
|
||||||
|
from django.utils.encoding import force_text
|
||||||
|
from model_mommy import mommy
|
||||||
|
from selenium.common.exceptions import NoSuchElementException
|
||||||
|
from six import text_type
|
||||||
|
|
||||||
|
from django_select2.cache import cache
|
||||||
|
from django_select2.forms import (
|
||||||
|
HeavySelect2Widget, ModelSelect2TagWidget, ModelSelect2Widget,
|
||||||
|
Select2Widget
|
||||||
|
)
|
||||||
|
from tests.testapp import forms
|
||||||
|
from tests.testapp.forms import NUMBER_CHOICES, HeavySelect2MultipleWidgetForm
|
||||||
|
from tests.testapp.models import Genre
|
||||||
|
|
||||||
|
|
||||||
|
class TestSelect2Mixin(object):
|
||||||
|
url = reverse('select2_widget')
|
||||||
|
form = forms.AlbumSelect2WidgetForm()
|
||||||
|
widget_cls = Select2Widget
|
||||||
|
|
||||||
|
def test_initial_form_class(self):
|
||||||
|
widget = self.widget_cls(attrs={'class': 'my-class'})
|
||||||
|
assert 'my-class' in widget.render('name', None)
|
||||||
|
assert 'django-select2' in widget.render('name', None)
|
||||||
|
|
||||||
|
def test_allow_clear(self, db):
|
||||||
|
required_field = self.form.fields['artist']
|
||||||
|
assert required_field.required is True
|
||||||
|
assert 'data-allow-clear="true"' not in required_field.widget.render('artist', None)
|
||||||
|
assert 'data-allow-clear="false"' in required_field.widget.render('artist', None)
|
||||||
|
assert '<option></option>' not in required_field.widget.render('artist', None)
|
||||||
|
|
||||||
|
not_required_field = self.form.fields['primary_genre']
|
||||||
|
assert not_required_field.required is False
|
||||||
|
assert 'data-allow-clear="true"' in not_required_field.widget.render('primary_genre', None)
|
||||||
|
assert 'data-allow-clear="false"' not in not_required_field.widget.render('primary_genre',
|
||||||
|
None)
|
||||||
|
assert 'data-placeholder' in not_required_field.widget.render('primary_genre', None)
|
||||||
|
assert '<option></option>' in not_required_field.widget.render('primary_genre', None)
|
||||||
|
|
||||||
|
def test_no_js_error(self, db, live_server, driver):
|
||||||
|
driver.get(live_server + self.url)
|
||||||
|
with pytest.raises(NoSuchElementException):
|
||||||
|
error = driver.find_element_by_xpath('//body[@JSError]')
|
||||||
|
pytest.fail(error.get_attribute('JSError'))
|
||||||
|
|
||||||
|
def test_selecting(self, db, live_server, driver):
|
||||||
|
driver.get(live_server + self.url)
|
||||||
|
with pytest.raises(NoSuchElementException):
|
||||||
|
driver.find_element_by_css_selector('.select2-results')
|
||||||
|
elem = driver.find_element_by_css_selector('.select2-selection')
|
||||||
|
elem.click()
|
||||||
|
results = driver.find_element_by_css_selector('.select2-results')
|
||||||
|
assert results.is_displayed() is True
|
||||||
|
elem = results.find_element_by_css_selector('.select2-results__option')
|
||||||
|
elem.click()
|
||||||
|
|
||||||
|
with pytest.raises(NoSuchElementException):
|
||||||
|
error = driver.find_element_by_xpath('//body[@JSError]')
|
||||||
|
pytest.fail(error.get_attribute('JSError'))
|
||||||
|
|
||||||
|
def test_data_url(self):
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
HeavySelect2Widget()
|
||||||
|
|
||||||
|
widget = HeavySelect2Widget(data_url='/foo/bar')
|
||||||
|
assert widget.get_url() == '/foo/bar'
|
||||||
|
|
||||||
|
|
||||||
|
class TestHeavySelect2Mixin(TestSelect2Mixin):
|
||||||
|
url = reverse('heavy_select2_widget')
|
||||||
|
form = forms.HeavySelect2WidgetForm(initial={'primary_genre': [1]})
|
||||||
|
widget_cls = HeavySelect2Widget
|
||||||
|
|
||||||
|
def test_initial_form_class(self):
|
||||||
|
widget = self.widget_cls(data_view='heavy_data', attrs={'class': 'my-class'})
|
||||||
|
assert 'my-class' in widget.render('name', None)
|
||||||
|
assert 'django-select2' in widget.render('name', None)
|
||||||
|
assert 'django-select2-heavy' in widget.render('name', None), widget.render('name', None)
|
||||||
|
|
||||||
|
def test_selected_option(self, db):
|
||||||
|
not_required_field = self.form.fields['primary_genre']
|
||||||
|
assert not_required_field.required is False
|
||||||
|
assert '<option value="1" selected="selected">One</option>' in \
|
||||||
|
not_required_field.widget.render('primary_genre', 1, choices=NUMBER_CHOICES), \
|
||||||
|
not_required_field.widget.render('primary_genre', 1, choices=NUMBER_CHOICES)
|
||||||
|
|
||||||
|
def test_many_selected_option(self, db, genres):
|
||||||
|
field = HeavySelect2MultipleWidgetForm().fields['genres']
|
||||||
|
widget_output = field.widget.render(
|
||||||
|
'genres', [1, 2],
|
||||||
|
choices=NUMBER_CHOICES)
|
||||||
|
selected_option = '<option value="{pk}" selected="selected">{value}</option>'.format(pk=1, value='One')
|
||||||
|
selected_option2 = '<option value="{pk}" selected="selected">{value}</option>'.format(pk=2, value='Two')
|
||||||
|
|
||||||
|
assert selected_option in widget_output, widget_output
|
||||||
|
assert selected_option2 in widget_output
|
||||||
|
|
||||||
|
|
||||||
|
class TestModelSelect2Mixin(TestHeavySelect2Mixin):
|
||||||
|
form = forms.AlbumModelSelect2WidgetForm(initial={'primary_genre': 1})
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def genres(self, db):
|
||||||
|
return mommy.make(Genre, 100)
|
||||||
|
|
||||||
|
def test_selected_option(self, db, genres):
|
||||||
|
genre = genres[0]
|
||||||
|
genre2 = genres[1]
|
||||||
|
not_required_field = self.form.fields['primary_genre']
|
||||||
|
assert not_required_field.required is False
|
||||||
|
widget_output = not_required_field.widget.render(
|
||||||
|
'primary_genre', genre.pk)
|
||||||
|
selected_option = '<option value="{pk}" selected="selected">{value}</option>'.format(
|
||||||
|
pk=genre.pk, value=force_text(genre))
|
||||||
|
unselected_option = '<option value="{pk}">{value}</option>'.format(
|
||||||
|
pk=genre2.pk, value=force_text(genre2))
|
||||||
|
|
||||||
|
assert selected_option in widget_output, widget_output
|
||||||
|
assert unselected_option not in widget_output
|
||||||
|
|
||||||
|
def test_get_queryset(self):
|
||||||
|
widget = ModelSelect2Widget()
|
||||||
|
with pytest.raises(NotImplementedError):
|
||||||
|
widget.get_queryset()
|
||||||
|
widget.model = Genre
|
||||||
|
assert isinstance(widget.get_queryset(), QuerySet)
|
||||||
|
widget.model = None
|
||||||
|
widget.queryset = Genre.objects.all()
|
||||||
|
assert isinstance(widget.get_queryset(), QuerySet)
|
||||||
|
|
||||||
|
def test_get_search_fields(self):
|
||||||
|
widget = ModelSelect2Widget()
|
||||||
|
with pytest.raises(NotImplementedError):
|
||||||
|
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())
|
||||||
|
|
||||||
|
def test_model_kwarg(self):
|
||||||
|
widget = ModelSelect2Widget(model=Genre, search_fields=['title__icontains'])
|
||||||
|
genre = Genre.objects.last()
|
||||||
|
result = widget.filter_queryset(genre.title)
|
||||||
|
assert result.exists()
|
||||||
|
|
||||||
|
def test_queryset_kwarg(self):
|
||||||
|
widget = ModelSelect2Widget(queryset=Genre.objects, search_fields=['title__icontains'])
|
||||||
|
genre = Genre.objects.last()
|
||||||
|
result = widget.filter_queryset(genre.title)
|
||||||
|
assert result.exists()
|
||||||
|
|
||||||
|
def test_ajax_view_registration(self, client):
|
||||||
|
widget = ModelSelect2Widget(queryset=Genre.objects, search_fields=['title__icontains'])
|
||||||
|
widget.render('name', 'value')
|
||||||
|
url = reverse('django_select2-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
|
||||||
|
data = json.loads(response.content.decode('utf-8'))
|
||||||
|
assert data['results']
|
||||||
|
assert genre.pk in [result['id'] for result in data['results']]
|
||||||
|
|
||||||
|
def test_render(self):
|
||||||
|
widget = ModelSelect2Widget()
|
||||||
|
widget.render('name', 'value')
|
||||||
|
cached_widget = cache.get(widget._get_cache_key())
|
||||||
|
assert isinstance(cached_widget, ModelSelect2Widget)
|
||||||
|
|
||||||
|
|
||||||
|
class TestHeavySelect2TagWidget(TestHeavySelect2Mixin):
|
||||||
|
|
||||||
|
def test_tag_attrs(self):
|
||||||
|
widget = ModelSelect2TagWidget(queryset=Genre.objects, search_fields=['title__icontains'])
|
||||||
|
output = widget.render('name', 'value')
|
||||||
|
assert 'data-minimum-input-length="1"' in output
|
||||||
|
assert 'data-tags="true"' in output
|
||||||
|
assert 'data-token-separators' in output
|
||||||
43
tests/test_views.py
Normal file
43
tests/test_views.py
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
from django.core import signing
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.utils.encoding import smart_text
|
||||||
|
|
||||||
|
from tests.testapp.forms import AlbumModelSelect2WidgetForm
|
||||||
|
|
||||||
|
|
||||||
|
class TestAutoResponseView(object):
|
||||||
|
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')
|
||||||
|
response = client.get(url, {'field_id': field_id, 'term': artist.title})
|
||||||
|
assert response.status_code == 200
|
||||||
|
data = json.loads(response.content.decode('utf-8'))
|
||||||
|
assert data['results']
|
||||||
|
assert {'id': artist.pk, 'text': smart_text(artist)} in data['results']
|
||||||
|
|
||||||
|
def test_no_field_id(self, client, artists):
|
||||||
|
artist = artists[0]
|
||||||
|
url = reverse('django_select2-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')
|
||||||
|
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')
|
||||||
|
response = client.get(url, {'field_id': field_id, 'term': artist.title})
|
||||||
|
assert response.status_code == 404
|
||||||
|
|
@ -1,137 +0,0 @@
|
||||||
# -*- coding:utf-8 -*-
|
|
||||||
from __future__ import print_function, unicode_literals
|
|
||||||
|
|
||||||
import json
|
|
||||||
from collections import Iterable
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from django.core import signing
|
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from django.db.models import QuerySet
|
|
||||||
from model_mommy import mommy
|
|
||||||
from selenium.common.exceptions import NoSuchElementException
|
|
||||||
from six import text_type
|
|
||||||
|
|
||||||
from django_select2.cache import cache
|
|
||||||
from django_select2.forms import ModelSelect2Widget
|
|
||||||
from tests.testapp.models import Genre
|
|
||||||
|
|
||||||
|
|
||||||
class TestWidgets(object):
|
|
||||||
url = ""
|
|
||||||
|
|
||||||
def test_is_hidden_multiple(self):
|
|
||||||
from django_select2.forms import HeavySelect2MultipleWidget
|
|
||||||
new_widget = HeavySelect2MultipleWidget(data_url="/")
|
|
||||||
assert new_widget.is_hidden is False
|
|
||||||
|
|
||||||
def test_is_hidden(self):
|
|
||||||
from django_select2.forms import HeavySelect2Widget
|
|
||||||
new_widget = HeavySelect2Widget(data_url="/")
|
|
||||||
assert new_widget.is_hidden is False
|
|
||||||
|
|
||||||
|
|
||||||
class TestSelect2Widget(object):
|
|
||||||
url = reverse('select2_widget')
|
|
||||||
|
|
||||||
def test_selecting(self, db, client, live_server, driver):
|
|
||||||
driver.get(live_server + self.url)
|
|
||||||
dropdown = driver.find_element_by_css_selector('.select2-results')
|
|
||||||
assert dropdown.is_displayed() is False
|
|
||||||
elem = driver.find_element_by_css_selector('.select2-choice')
|
|
||||||
elem.click()
|
|
||||||
assert dropdown.is_displayed() is True
|
|
||||||
with pytest.raises(NoSuchElementException):
|
|
||||||
error = driver.find_element_by_xpath('//body[@JSError]')
|
|
||||||
pytest.fail(error.get_attribute('JSError'))
|
|
||||||
|
|
||||||
|
|
||||||
class TestHeavySelect2Widget(object):
|
|
||||||
url = reverse('heavy_select2_widget')
|
|
||||||
|
|
||||||
def test_heavy_select(self, db, client, live_server, driver):
|
|
||||||
driver.get(live_server + self.url)
|
|
||||||
dropdown = driver.find_element_by_css_selector('.select2-results')
|
|
||||||
assert dropdown.is_displayed() is False
|
|
||||||
elem = driver.find_element_by_css_selector('.select2-choice')
|
|
||||||
elem.click()
|
|
||||||
assert dropdown.is_displayed() is True
|
|
||||||
with pytest.raises(NoSuchElementException):
|
|
||||||
error = driver.find_element_by_xpath('//body[@JSError]')
|
|
||||||
pytest.fail(error.get_attribute('JSError'))
|
|
||||||
|
|
||||||
|
|
||||||
class TestHeavySelect2MultipleWidget(object):
|
|
||||||
url = reverse('heavy_select2_multiple_widget')
|
|
||||||
|
|
||||||
def test_heavy_select_multiple(self, db, client, live_server, driver):
|
|
||||||
driver.get(live_server + self.url)
|
|
||||||
dropdown = driver.find_element_by_css_selector('.select2-results')
|
|
||||||
assert dropdown.is_displayed() is False
|
|
||||||
elem = driver.find_element_by_css_selector('.select2-choices')
|
|
||||||
elem.click()
|
|
||||||
assert dropdown.is_displayed() is True
|
|
||||||
with pytest.raises(NoSuchElementException):
|
|
||||||
error = driver.find_element_by_xpath('//body[@JSError]')
|
|
||||||
pytest.fail(error.get_attribute('JSError'))
|
|
||||||
|
|
||||||
|
|
||||||
class TestHeavySelect2Mixin(object):
|
|
||||||
@pytest.fixture(autouse=True)
|
|
||||||
def genres(self, db):
|
|
||||||
mommy.make(Genre, 100)
|
|
||||||
|
|
||||||
def test_get_queryset(self):
|
|
||||||
widget = ModelSelect2Widget()
|
|
||||||
with pytest.raises(NotImplementedError):
|
|
||||||
widget.get_queryset()
|
|
||||||
widget.model = Genre
|
|
||||||
assert isinstance(widget.get_queryset(), QuerySet)
|
|
||||||
widget.model = None
|
|
||||||
widget.queryset = Genre.objects.all()
|
|
||||||
assert isinstance(widget.get_queryset(), QuerySet)
|
|
||||||
|
|
||||||
def test_get_search_fields(self):
|
|
||||||
widget = ModelSelect2Widget()
|
|
||||||
with pytest.raises(NotImplementedError):
|
|
||||||
widget.get_search_fields()
|
|
||||||
|
|
||||||
widget.search_fields = ['title__icontains']
|
|
||||||
assert isinstance(widget.get_search_fields(), Iterable)
|
|
||||||
assert all(isinstance(x, text_type) for x in widget.get_search_fields())
|
|
||||||
|
|
||||||
def test_model_kwarg(self):
|
|
||||||
widget = ModelSelect2Widget(model=Genre, search_fields=['title__icontains'])
|
|
||||||
genre = Genre.objects.last()
|
|
||||||
result = widget.filter_queryset(genre.title)
|
|
||||||
assert result.exists()
|
|
||||||
|
|
||||||
def test_queryset_kwarg(self):
|
|
||||||
widget = ModelSelect2Widget(queryset=Genre.objects, search_fields=['title__icontains'])
|
|
||||||
genre = Genre.objects.last()
|
|
||||||
result = widget.filter_queryset(genre.title)
|
|
||||||
assert result.exists()
|
|
||||||
|
|
||||||
def test_widget_id(self):
|
|
||||||
widget = ModelSelect2Widget()
|
|
||||||
widget.render('name', 'value')
|
|
||||||
assert widget.widget_id
|
|
||||||
assert signing.loads(widget.widget_id) == id(widget)
|
|
||||||
|
|
||||||
def test_render(self):
|
|
||||||
widget = ModelSelect2Widget()
|
|
||||||
widget.render('name', 'value')
|
|
||||||
cached_widget = cache.get(widget._get_cache_key())
|
|
||||||
assert isinstance(cached_widget, ModelSelect2Widget)
|
|
||||||
assert cached_widget.widget_id == widget.widget_id
|
|
||||||
|
|
||||||
def test_ajax_view_registration(self, client):
|
|
||||||
widget = ModelSelect2Widget(queryset=Genre.objects, search_fields=['title__icontains'])
|
|
||||||
widget.render('name', 'value')
|
|
||||||
url = reverse('django_select2_central_json')
|
|
||||||
genre = Genre.objects.last()
|
|
||||||
response = client.get(url, data=dict(field_id=widget.widget_id, term=genre.title))
|
|
||||||
assert response.status_code == 200, response.content
|
|
||||||
data = json.loads(response.content.decode('utf-8'))
|
|
||||||
assert data['results']
|
|
||||||
assert genre.pk in [result['id'] for result in data['results']]
|
|
||||||
|
|
@ -4,76 +4,137 @@ from __future__ import absolute_import, unicode_literals
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from django_select2.forms import (
|
from django_select2.forms import (
|
||||||
HeavySelect2MultipleWidget, HeavySelect2Widget, Select2MultipleWidget,
|
HeavySelect2MultipleWidget, HeavySelect2Widget, ModelSelect2MultipleWidget,
|
||||||
Select2Widget,
|
ModelSelect2TagWidget, ModelSelect2Widget, Select2MultipleWidget,
|
||||||
ModelSelect2MultipleWidget, ModelSelect2Widget)
|
Select2Widget
|
||||||
|
)
|
||||||
from tests.testapp import models
|
from tests.testapp import models
|
||||||
|
from tests.testapp.models import Album
|
||||||
|
|
||||||
|
|
||||||
class GenreModelForm(forms.ModelForm):
|
class TitleSearchFieldMixin(object):
|
||||||
|
search_fields = [
|
||||||
|
'title__icontains'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class TitleModelSelect2Widget(TitleSearchFieldMixin, ModelSelect2Widget):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TitleModelSelect2MultipleWidget(TitleSearchFieldMixin, ModelSelect2MultipleWidget):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class GenreSelect2TagWidget(TitleSearchFieldMixin, ModelSelect2TagWidget):
|
||||||
|
model = models.Genre
|
||||||
|
|
||||||
|
def create_value(self, value):
|
||||||
|
self.get_queryset().create(title=value)
|
||||||
|
|
||||||
|
|
||||||
|
class AlbumSelect2WidgetForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.Genre
|
model = models.Album
|
||||||
fields = (
|
fields = (
|
||||||
'title',
|
'artist',
|
||||||
)
|
'primary_genre',
|
||||||
|
|
||||||
|
|
||||||
class GenreForm(forms.Form):
|
|
||||||
title = forms.CharField(max_length=50)
|
|
||||||
|
|
||||||
|
|
||||||
class ArtistModelForm(forms.ModelForm):
|
|
||||||
test = forms.BooleanField('asdf')
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = models.Artist
|
|
||||||
fields = (
|
|
||||||
'title',
|
|
||||||
'genres',
|
|
||||||
)
|
)
|
||||||
widgets = {
|
widgets = {
|
||||||
'genres': Select2MultipleWidget
|
'artist': Select2Widget,
|
||||||
|
'primary_genre': Select2Widget,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ArtistForm(forms.Form):
|
class AlbumSelect2MultipleWidgetForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = models.Album
|
||||||
|
fields = (
|
||||||
|
'genres',
|
||||||
|
'featured_artists',
|
||||||
|
)
|
||||||
|
widgets = {
|
||||||
|
'genres': Select2MultipleWidget,
|
||||||
|
'featured_artists': Select2MultipleWidget,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AlbumModelSelect2WidgetForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = models.Album
|
||||||
|
fields = (
|
||||||
|
'artist',
|
||||||
|
'primary_genre',
|
||||||
|
)
|
||||||
|
widgets = {
|
||||||
|
'artist': ModelSelect2Widget(
|
||||||
|
model=models.Artist,
|
||||||
|
search_fields=['title__icontains']
|
||||||
|
),
|
||||||
|
'primary_genre': ModelSelect2Widget(
|
||||||
|
model=models.Genre,
|
||||||
|
search_fields=['title__icontains']
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AlbumModelSelect2MultipleWidgetRequiredForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Album
|
||||||
|
fields = (
|
||||||
|
'genres',
|
||||||
|
'featured_artists',
|
||||||
|
)
|
||||||
|
widgets = {
|
||||||
|
'genres': TitleModelSelect2MultipleWidget,
|
||||||
|
'featured_artists': TitleModelSelect2MultipleWidget,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ArtistModelSelect2MultipleWidgetForm(forms.Form):
|
||||||
title = forms.CharField(max_length=50)
|
title = forms.CharField(max_length=50)
|
||||||
genres = forms.ModelMultipleChoiceField(widget=ModelSelect2MultipleWidget(
|
genres = forms.ModelMultipleChoiceField(widget=ModelSelect2MultipleWidget(
|
||||||
queryset=models.Genre.objects.all(),
|
queryset=models.Genre.objects.all(),
|
||||||
search_fields=['title__icontains'],
|
search_fields=['title__icontains'],
|
||||||
), queryset=models.Genre.objects.all())
|
), queryset=models.Genre.objects.all(), required=False)
|
||||||
|
|
||||||
|
NUMBER_CHOICES = [
|
||||||
class AlbumModelForm(forms.ModelForm):
|
(1, 'One'),
|
||||||
class Meta:
|
(2, 'Two'),
|
||||||
model = models.Album
|
(3, 'Three'),
|
||||||
fields = (
|
(4, 'Four'),
|
||||||
'title',
|
]
|
||||||
'artist',
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class AlbumForm(forms.Form):
|
|
||||||
title = forms.CharField(max_length=255)
|
|
||||||
artist = forms.ModelChoiceField(widget=ModelSelect2Widget(
|
|
||||||
model=models.Artist,
|
|
||||||
search_fields=['title__icontains']
|
|
||||||
), queryset=models.Artist.objects.all())
|
|
||||||
|
|
||||||
|
|
||||||
class Select2WidgetForm(forms.Form):
|
class Select2WidgetForm(forms.Form):
|
||||||
NUMBER_CHOICES = [
|
number = forms.ChoiceField(widget=Select2Widget, choices=NUMBER_CHOICES, required=False)
|
||||||
(1, 'One'),
|
|
||||||
(2, 'Two'),
|
|
||||||
(3, 'Three'),
|
|
||||||
(4, 'Four'),
|
|
||||||
]
|
|
||||||
number = forms.ChoiceField(widget=Select2Widget(), choices=NUMBER_CHOICES)
|
|
||||||
|
|
||||||
|
|
||||||
class HeavySelect2WidgetForm(forms.Form):
|
class HeavySelect2WidgetForm(forms.Form):
|
||||||
heavy_number = forms.ChoiceField(widget=HeavySelect2Widget(data_view='heavy_data'))
|
artist = forms.ChoiceField(
|
||||||
|
widget=HeavySelect2Widget(data_view='heavy_data', choices=NUMBER_CHOICES)
|
||||||
|
)
|
||||||
|
primary_genre = forms.ChoiceField(
|
||||||
|
widget=HeavySelect2Widget(data_view='heavy_data', choices=NUMBER_CHOICES),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class HeavySelect2MultipleWidgetForm(forms.Form):
|
class HeavySelect2MultipleWidgetForm(forms.Form):
|
||||||
heavy_number = forms.MultipleChoiceField(widget=HeavySelect2MultipleWidget(data_view='heavy_data'))
|
genres = forms.MultipleChoiceField(
|
||||||
|
widget=HeavySelect2MultipleWidget(data_view='heavy_data', choices=NUMBER_CHOICES)
|
||||||
|
)
|
||||||
|
featured_artists = forms.MultipleChoiceField(
|
||||||
|
widget=HeavySelect2MultipleWidget(data_view='heavy_data', choices=NUMBER_CHOICES),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ModelSelect2TagWidgetForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Album
|
||||||
|
fields = ['genres']
|
||||||
|
widgets = {
|
||||||
|
'genres': GenreSelect2TagWidget
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,17 +26,9 @@ class Artist(models.Model):
|
||||||
class Album(models.Model):
|
class Album(models.Model):
|
||||||
title = models.CharField(max_length=255)
|
title = models.CharField(max_length=255)
|
||||||
artist = models.ForeignKey(Artist)
|
artist = models.ForeignKey(Artist)
|
||||||
|
featured_artists = models.ManyToManyField(Artist, blank=True, related_name='featured_album_set')
|
||||||
def __str__(self):
|
primary_genre = models.ForeignKey(Genre, blank=True, null=True, related_name='primary_album_set')
|
||||||
return self.title
|
genres = models.ManyToManyField(Genre)
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
|
||||||
class Song(models.Model):
|
|
||||||
title = models.CharField(max_length=255)
|
|
||||||
album = models.ForeignKey(Album, blank=True, null=True)
|
|
||||||
artist = models.ForeignKey(Artist)
|
|
||||||
genres = models.ManyToManyField(Genre, blank=True, null=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.title
|
return self.title
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ DEBUG = True
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
'NAME': 'testdb.sqlite',
|
'NAME': ':memory:',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -48,5 +48,3 @@ if os.environ.get('TRAVIS'):
|
||||||
'LOCATION': 'localhost:11211',
|
'LOCATION': 'localhost:11211',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SELECT2_BOOTSTRAP = True
|
|
||||||
4
tests/testapp/static/jquery-2.1.4.min.js
vendored
4
tests/testapp/static/jquery-2.1.4.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -3,7 +3,11 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
{{ form.media.css }}
|
{{ form.media.css }}
|
||||||
<style type="text/css">select{width:200px}</style>
|
<style type="text/css">
|
||||||
|
select {
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<form method="post" action="">
|
<form method="post" action="">
|
||||||
|
|
@ -11,7 +15,7 @@
|
||||||
{{ form }}
|
{{ form }}
|
||||||
<input type="submit" value="Submit Form"/>
|
<input type="submit" value="Submit Form"/>
|
||||||
</form>
|
</form>
|
||||||
<script src="{% static 'jquery-2.1.4.min.js' %}"></script>
|
<script src="{% static '//code.jquery.com/jquery-2.1.4.min.js' %}"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
window.onerror = function (msg) {
|
window.onerror = function (msg) {
|
||||||
$("body").attr("JSError", msg);
|
$("body").attr("JSError", msg);
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ from __future__ import absolute_import, unicode_literals
|
||||||
from django.conf.urls import include, patterns, url
|
from django.conf.urls import include, patterns, url
|
||||||
|
|
||||||
from .forms import (
|
from .forms import (
|
||||||
ArtistForm, HeavySelect2MultipleWidgetForm, HeavySelect2WidgetForm,
|
AlbumModelSelect2WidgetForm, HeavySelect2MultipleWidgetForm,
|
||||||
Select2WidgetForm
|
HeavySelect2WidgetForm, ModelSelect2TagWidgetForm, Select2WidgetForm
|
||||||
)
|
)
|
||||||
from .views import TemplateFormView, heavy_data
|
from .views import TemplateFormView, heavy_data
|
||||||
|
|
||||||
|
|
@ -16,11 +16,18 @@ urlpatterns = patterns(
|
||||||
url(r'^heavy_select2_widget/$',
|
url(r'^heavy_select2_widget/$',
|
||||||
TemplateFormView.as_view(form_class=HeavySelect2WidgetForm), name='heavy_select2_widget'),
|
TemplateFormView.as_view(form_class=HeavySelect2WidgetForm), name='heavy_select2_widget'),
|
||||||
url(r'^heavy_select2_multiple_widget/$',
|
url(r'^heavy_select2_multiple_widget/$',
|
||||||
TemplateFormView.as_view(form_class=HeavySelect2MultipleWidgetForm), name='heavy_select2_multiple_widget'),
|
TemplateFormView.as_view(form_class=HeavySelect2MultipleWidgetForm),
|
||||||
url(r'^single_value_model_field/$',
|
name='heavy_select2_multiple_widget'),
|
||||||
TemplateFormView.as_view(form_class=ArtistForm), name='single_value_model_field'),
|
|
||||||
url(r'^heavy_data/$',
|
url(r'^model_select2_widget/$',
|
||||||
heavy_data, name='heavy_data'),
|
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'),
|
||||||
|
|
||||||
|
url(r'^heavy_data/$', heavy_data, name='heavy_data'),
|
||||||
|
|
||||||
url(r'^select2/', include('django_select2.urls')),
|
url(r'^select2/', include('django_select2.urls')),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue