mirror of
https://github.com/jazzband/django-admin2.git
synced 2026-03-16 22:20:24 +00:00
Merge pull request #403 from galuszkak/tox_tests
Many fixes to py3 support and django 1.7 compatybility
This commit is contained in:
commit
59990bbef5
69 changed files with 375 additions and 569 deletions
|
|
@ -2,21 +2,20 @@ language: python
|
|||
python: "2.7"
|
||||
env:
|
||||
matrix:
|
||||
- TOX_ENV=py27-dj1.4.x
|
||||
- TOX_ENV=py27-dj1.5.x
|
||||
- TOX_ENV=py27-dj1.6.x
|
||||
- TOX_ENV=py27-dj1.7.x
|
||||
- TOX_ENV=py33-dj1.6.x
|
||||
- TOX_ENV=py34-dj1.6.x
|
||||
- TOX_ENV=py33-dj1.7.x
|
||||
- TOX_ENV=py34-dj1.7.x
|
||||
- TOX_ENV=pypy-dj1.6.x,
|
||||
- TOX_ENV=pypy-dj1.6.x
|
||||
- TOX_ENV=pypy3-dj1.6.x
|
||||
install:
|
||||
- pip install tox
|
||||
script:
|
||||
- tox -e $TOX_ENV
|
||||
#for now commented
|
||||
# for now commented. We have to figure which version use for coverage
|
||||
# and coveralls
|
||||
#after_success:
|
||||
# - coverage report
|
||||
# - pip install --quiet python-coveralls
|
||||
|
|
|
|||
24
.tx/config
24
.tx/config
|
|
@ -1,24 +0,0 @@
|
|||
[main]
|
||||
host = https://www.transifex.com
|
||||
lang_map = sr@latin:sr_Latn
|
||||
|
||||
[django-admin2.djadmin2po]
|
||||
file_filter = djadmin2/locale/<lang>/LC_MESSAGES/django.po
|
||||
source_file = djadmin2/locale/en/LC_MESSAGES/django.po
|
||||
source_lang = en_US
|
||||
type = PO
|
||||
minimum_perc = 80
|
||||
|
||||
[django-admin2.example-app]
|
||||
file_filter = example/blog/locale/<lang>/LC_MESSAGES/django.po
|
||||
source_file = example/blog/locale/en/LC_MESSAGES/django.po
|
||||
source_lang = en_US
|
||||
type = PO
|
||||
minimum_perc = 50
|
||||
|
||||
[django-admin2.example2-app]
|
||||
file_filter = example2/polls/locale/<lang>/LC_MESSAGES/django.po
|
||||
source_file = example2/polls/locale/en/LC_MESSAGES/django.po
|
||||
source_lang = en_US
|
||||
type = PO
|
||||
minimum_perc = 50
|
||||
|
|
@ -50,6 +50,7 @@ Developers
|
|||
* James Rivett-Carnac (@yarbelk / james.rivettcarnac@gmail.com)
|
||||
* Andrew Mosson (@amosson / amosson@tippit.com)
|
||||
* marangonico
|
||||
* Kamil Gałuszka (@galuszkak / galuszkak@gmail.com)
|
||||
|
||||
Translators
|
||||
-----------
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ Screenshots
|
|||
Requirements
|
||||
=============
|
||||
|
||||
* Django 1.5+
|
||||
* Django 1.6+
|
||||
* Python 2.7+ or Python 3.3+
|
||||
* django-braces_
|
||||
* django-extra-views_
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ class BaseListAction(AdminModel2Mixin, TemplateView):
|
|||
objects_name = options.verbose_name
|
||||
else:
|
||||
objects_name = options.verbose_name_plural
|
||||
self.objects_name = unicode(objects_name)
|
||||
self.objects_name = force_text(objects_name)
|
||||
|
||||
super(BaseListAction, self).__init__(*args, **kwargs)
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class Admin2APISerializer(serializers.HyperlinkedModelSerializer):
|
|||
_default_view_name = 'admin2:%(app_label)s_%(model_name)s_api_detail'
|
||||
|
||||
pk = fields.Field(source='pk')
|
||||
__str__ = fields.Field(source='__unicode__')
|
||||
__unicode__ = fields.Field(source='__str__')
|
||||
|
||||
|
||||
class Admin2APIMixin(Admin2Mixin):
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ from __future__ import division, absolute_import, unicode_literals
|
|||
from django.conf.urls import patterns, include, url
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils.importlib import import_module
|
||||
|
||||
from importlib import import_module
|
||||
|
||||
|
||||
from . import apiviews
|
||||
|
|
@ -122,7 +123,7 @@ class Admin2(object):
|
|||
try:
|
||||
import_module("%s.admin2" % app_name)
|
||||
except ImportError as e:
|
||||
if str(e) == "No module named admin2":
|
||||
if str(e).startswith("No module named") and 'admin2' in str(e):
|
||||
continue
|
||||
raise e
|
||||
|
||||
|
|
@ -188,7 +189,7 @@ class Admin2(object):
|
|||
name='api_index'
|
||||
),
|
||||
)
|
||||
for model, model_admin in self.registry.iteritems():
|
||||
for model, model_admin in self.registry.items():
|
||||
model_options = utils.model_options(model)
|
||||
urlpatterns += patterns(
|
||||
'',
|
||||
|
|
@ -207,3 +208,4 @@ class Admin2(object):
|
|||
def urls(self):
|
||||
# We set the application and instance namespace here
|
||||
return self.get_urls(), self.name, self.name
|
||||
|
||||
|
|
|
|||
|
|
@ -2,18 +2,22 @@
|
|||
from __future__ import division, absolute_import, unicode_literals
|
||||
|
||||
import collections
|
||||
|
||||
from itertools import chain
|
||||
|
||||
from django import forms
|
||||
from django.forms.util import flatatt
|
||||
from django.utils.html import format_html
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.encoding import force_text, force_bytes
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.forms import widgets as django_widgets
|
||||
from django.utils import six
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
import django_filters
|
||||
|
||||
from .utils import type_str
|
||||
|
||||
LINK_TEMPLATE = '<a href=?{0}={1} {2}>{3}</a>'
|
||||
|
||||
|
||||
|
|
@ -74,7 +78,7 @@ def build_list_filter(request, model_admin, queryset):
|
|||
# otherwise build :mod:`django_filters.FilterSet`
|
||||
filters = []
|
||||
for field_filter in model_admin.list_filter:
|
||||
if isinstance(field_filter, basestring):
|
||||
if isinstance(field_filter, six.string_types):
|
||||
filters.append(get_filter_for_field_name(
|
||||
queryset.model,
|
||||
field_filter,
|
||||
|
|
@ -84,20 +88,16 @@ def build_list_filter(request, model_admin, queryset):
|
|||
filterset_dict = {}
|
||||
for field_filter in filters:
|
||||
filterset_dict[field_filter.name] = field_filter
|
||||
fields = filterset_dict.keys()
|
||||
fields = list(filterset_dict.keys())
|
||||
filterset_dict['Meta'] = type(
|
||||
b'Meta',
|
||||
type_str('Meta'),
|
||||
(),
|
||||
{
|
||||
'model': queryset.model,
|
||||
'fields': fields,
|
||||
},
|
||||
)
|
||||
return type(
|
||||
b'%sFilterSet' % queryset.model.__name__,
|
||||
(django_filters.FilterSet, ),
|
||||
filterset_dict,
|
||||
)(request.GET, queryset=queryset)
|
||||
return type(type_str('%sFilterSet' % queryset.model.__name__),(django_filters.FilterSet, ),filterset_dict,)(request.GET, queryset=queryset)
|
||||
|
||||
|
||||
def build_date_filter(request, model_admin, queryset):
|
||||
|
|
@ -117,7 +117,7 @@ def build_date_filter(request, model_admin, queryset):
|
|||
}
|
||||
|
||||
return type(
|
||||
b'%sDateFilterSet' % queryset.model.__name__,
|
||||
type_str('%sDateFilterSet' % queryset.model.__name__),
|
||||
(django_filters.FilterSet,),
|
||||
filterset_dict,
|
||||
)(request.GET, queryset=queryset)
|
||||
|
|
|
|||
|
|
@ -166,7 +166,9 @@ _django_to_floppyforms_widget = {
|
|||
django.forms.extras.widgets.SelectDateWidget:
|
||||
_create_widget(
|
||||
floppyforms.widgets.SelectDateWidget,
|
||||
init_arguments=('years', 'required')),
|
||||
init_arguments=
|
||||
('years',)
|
||||
if django.VERSION >= (1, 7) else ('years', 'required')),
|
||||
}
|
||||
|
||||
_django_field_to_floppyform_widget = {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ from django.db.models import get_models
|
|||
from django.utils import six
|
||||
|
||||
from . import utils
|
||||
from django.utils.encoding import python_2_unicode_compatible, force_text
|
||||
|
||||
|
||||
logger = logging.getLogger('djadmin2')
|
||||
|
|
@ -185,6 +186,7 @@ class ModelDeletePermission(BasePermission):
|
|||
permissions = (model_permission('{app_label}.delete_{model_name}'),)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class TemplatePermissionChecker(object):
|
||||
'''
|
||||
Can be used in the template like:
|
||||
|
|
@ -342,6 +344,12 @@ class TemplatePermissionChecker(object):
|
|||
def __nonzero__(self):
|
||||
# if no view is bound we will return false, since we don't know which
|
||||
# permission to check we stay save in disallowing the access
|
||||
return self._cast_bool()
|
||||
|
||||
def __bool__(self):
|
||||
return self._cast_bool()
|
||||
|
||||
def _cast_bool(self):
|
||||
if self._view is None:
|
||||
return False
|
||||
if self._obj is None:
|
||||
|
|
@ -349,10 +357,11 @@ class TemplatePermissionChecker(object):
|
|||
else:
|
||||
return self._view.has_permission(self._obj)
|
||||
|
||||
def __unicode__(self):
|
||||
|
||||
def __str__(self):
|
||||
if self._view is None:
|
||||
return ''
|
||||
return unicode(bool(self))
|
||||
return force_text(bool(self))
|
||||
|
||||
|
||||
def create_view_permissions(app, created_models, verbosity, **kwargs):
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from datetime import date, time, datetime
|
|||
|
||||
from django.db import models
|
||||
from django.utils import formats, timezone
|
||||
from django.utils.encoding import force_text
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
from djadmin2 import settings
|
||||
|
|
@ -60,10 +61,10 @@ def title_renderer(value, field):
|
|||
:type value: str or unicode
|
||||
:param field: The model field instance
|
||||
:type field: django.db.models.fields.Field
|
||||
:rtype: unicode
|
||||
:rtype: unicode or str
|
||||
|
||||
"""
|
||||
return unicode(value).title()
|
||||
return force_text(value).title()
|
||||
|
||||
|
||||
def number_renderer(value, field):
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
from test_admin2tags import *
|
||||
from test_types import *
|
||||
from test_utils import *
|
||||
from test_views import *
|
||||
from test_core import *
|
||||
from test_actions import *
|
||||
from test_auth_admin import *
|
||||
from test_renderers import *
|
||||
|
|
@ -9,7 +9,7 @@ from ..types import ModelAdmin2
|
|||
from ..core import Admin2
|
||||
|
||||
|
||||
class Thing(models.Model):
|
||||
class SmallThing(models.Model):
|
||||
pass
|
||||
|
||||
|
||||
|
|
@ -21,20 +21,20 @@ class Admin2Test(TestCase):
|
|||
self.admin2 = Admin2()
|
||||
|
||||
def test_register(self):
|
||||
self.admin2.register(Thing)
|
||||
self.assertTrue(isinstance(self.admin2.registry[Thing], ModelAdmin2))
|
||||
self.admin2.register(SmallThing)
|
||||
self.assertTrue(isinstance(self.admin2.registry[SmallThing], ModelAdmin2))
|
||||
|
||||
def test_register_error(self):
|
||||
self.admin2.register(Thing)
|
||||
self.assertRaises(ImproperlyConfigured, self.admin2.register, Thing)
|
||||
self.admin2.register(SmallThing)
|
||||
self.assertRaises(ImproperlyConfigured, self.admin2.register, SmallThing)
|
||||
|
||||
def test_deregister(self):
|
||||
self.admin2.register(Thing)
|
||||
self.admin2.deregister(Thing)
|
||||
self.assertTrue(Thing not in self.admin2.registry)
|
||||
self.admin2.register(SmallThing)
|
||||
self.admin2.deregister(SmallThing)
|
||||
self.assertTrue(SmallThing not in self.admin2.registry)
|
||||
|
||||
def test_deregister_error(self):
|
||||
self.assertRaises(ImproperlyConfigured, self.admin2.deregister, Thing)
|
||||
self.assertRaises(ImproperlyConfigured, self.admin2.deregister, SmallThing)
|
||||
|
||||
def test_register_app_verbose_name(self):
|
||||
self.admin2.register_app_verbose_name(APP_LABEL, APP_VERBOSE_NAME)
|
||||
|
|
@ -65,7 +65,7 @@ class Admin2Test(TestCase):
|
|||
)
|
||||
|
||||
def test_get_urls(self):
|
||||
self.admin2.register(Thing)
|
||||
self.admin2.register(SmallThing)
|
||||
self.assertEquals(8, len(self.admin2.get_urls()))
|
||||
|
||||
def test_default_entries(self):
|
||||
|
|
|
|||
|
|
@ -7,12 +7,13 @@ from decimal import Decimal
|
|||
from django.test import TestCase
|
||||
from django.db import models
|
||||
from django.utils.translation import activate
|
||||
from django.utils import six
|
||||
|
||||
from .. import renderers
|
||||
|
||||
|
||||
class RendererTestModel(models.Model):
|
||||
decimal = models.DecimalField(decimal_places=5)
|
||||
decimal = models.DecimalField(decimal_places=5, max_digits=10)
|
||||
|
||||
|
||||
class BooleanRendererTest(TestCase):
|
||||
|
|
@ -57,16 +58,16 @@ class DatetimeRendererTest(TestCase):
|
|||
|
||||
def test_time_german(self):
|
||||
activate('de')
|
||||
out = self.renderer(dt.time(13, 37, 01), None)
|
||||
out = self.renderer(dt.time(13, 37, 1), None)
|
||||
self.assertEqual('13:37:01', out)
|
||||
|
||||
def test_time_chinese(self):
|
||||
activate('zh')
|
||||
out = self.renderer(dt.time(13, 37, 01), None)
|
||||
out = self.renderer(dt.time(13, 37, 1), None)
|
||||
self.assertEqual('1:37 p.m.', out)
|
||||
|
||||
def test_datetime(self):
|
||||
out = self.renderer(dt.datetime(2013, 7, 6, 13, 37, 01), None)
|
||||
out = self.renderer(dt.datetime(2013, 7, 6, 13, 37, 1), None)
|
||||
self.assertEqual('July 6, 2013, 1:37 p.m.', out)
|
||||
|
||||
# TODO test timezone localization
|
||||
|
|
@ -105,7 +106,10 @@ class NumberRendererTest(TestCase):
|
|||
|
||||
def testEndlessFloat(self):
|
||||
out = self.renderer(1.0/3, None)
|
||||
self.assertEqual('0.333333333333', out)
|
||||
if six.PY2:
|
||||
self.assertEqual('0.333333333333', out)
|
||||
else:
|
||||
self.assertEqual('0.3333333333333333', out)
|
||||
|
||||
def testPlainDecimal(self):
|
||||
number = '0.123456789123456789123456789'
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class ImmutableAdminFactoryTests(TestCase):
|
|||
self.immutable_admin.d
|
||||
|
||||
|
||||
class Thing(models.Model):
|
||||
class BigThing(models.Model):
|
||||
pass
|
||||
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ class ModelAdminTest(TestCase):
|
|||
)
|
||||
|
||||
def test_get_index_kwargs(self):
|
||||
admin_instance = ModelAdmin2(Thing, Admin2)
|
||||
admin_instance = ModelAdmin2(BigThing, Admin2)
|
||||
self.assertIn(
|
||||
'paginate_by',
|
||||
admin_instance.get_index_kwargs().keys()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from django.db import models
|
||||
from django.test import TestCase
|
||||
from django.utils import six
|
||||
|
||||
from .. import utils
|
||||
from ..views import IndexView
|
||||
|
|
@ -154,10 +155,18 @@ class UtilsTest(TestCase):
|
|||
def __unicode__(self):
|
||||
return "unicode"
|
||||
|
||||
self.assertEquals(
|
||||
utils.get_attr(Klass(), "__str__"),
|
||||
"unicode"
|
||||
)
|
||||
if six.PY2:
|
||||
self.assertEquals(
|
||||
utils.get_attr(Klass(), "__str__"),
|
||||
"unicode"
|
||||
)
|
||||
else:
|
||||
self.assertEquals(
|
||||
utils.get_attr(Klass(), "__str__"),
|
||||
"str"
|
||||
)
|
||||
|
||||
|
||||
|
||||
def test_get_attr(self):
|
||||
class Klass(object):
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ class ModelAdmin2(with_metaclass(ModelAdminBase2)):
|
|||
'Cannot instantiate admin view "{}.{}". '
|
||||
'The error that got raised was: {}'.format(
|
||||
self.__class__.__name__, admin_view.name, e))
|
||||
raise new_exception, None, trace
|
||||
raise (new_exception, None, trace)
|
||||
pattern_list.append(
|
||||
url(
|
||||
regex=admin_view.url,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from django.db.models import ManyToManyRel
|
|||
from django.db.models.deletion import Collector
|
||||
from django.db.models.related import RelatedObject
|
||||
from django.utils import six
|
||||
|
||||
from django.utils.encoding import force_bytes, force_text
|
||||
|
||||
def lookup_needs_distinct(opts, lookup_path):
|
||||
"""
|
||||
|
|
@ -89,7 +89,10 @@ def get_attr(obj, attr):
|
|||
and the __str__ attribute.
|
||||
"""
|
||||
if attr == '__str__':
|
||||
value = unicode(obj)
|
||||
if six.PY2:
|
||||
value = unicode(obj)
|
||||
else:
|
||||
value = str(obj)
|
||||
else:
|
||||
attribute = getattr(obj, attr)
|
||||
value = attribute() if callable(attribute) else attribute
|
||||
|
|
@ -181,3 +184,10 @@ def quote(s):
|
|||
if c in """:/_#?;@&=+$,"<>%\\""":
|
||||
res[i] = '_%02X' % ord(c)
|
||||
return ''.join(res)
|
||||
|
||||
|
||||
def type_str(text):
|
||||
if six.PY2:
|
||||
return force_bytes(text)
|
||||
else:
|
||||
return force_text(text)
|
||||
|
|
@ -2,9 +2,11 @@
|
|||
from __future__ import division, absolute_import, unicode_literals
|
||||
|
||||
import operator
|
||||
from functools import reduce
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.forms import (PasswordChangeForm,
|
||||
AdminPasswordChangeForm)
|
||||
from django.contrib.auth.views import (logout as auth_logout,
|
||||
|
|
@ -19,16 +21,14 @@ from django.utils.encoding import force_text
|
|||
from django.utils.text import capfirst
|
||||
from django.utils.translation import ugettext_lazy
|
||||
from django.views import generic
|
||||
|
||||
import extra_views
|
||||
|
||||
|
||||
from . import permissions, utils
|
||||
from .forms import AdminAuthenticationForm
|
||||
from .models import LogEntry
|
||||
from .viewmixins import Admin2Mixin, AdminModel2Mixin, Admin2ModelFormMixin
|
||||
from .filters import build_list_filter, build_date_filter
|
||||
|
||||
from .models import LogEntry
|
||||
|
||||
class AdminView(object):
|
||||
|
||||
|
|
@ -292,14 +292,14 @@ class ModelListView(AdminModel2Mixin, generic.ListView):
|
|||
return context
|
||||
|
||||
def _format_years(self, context):
|
||||
years = context['object_list'].dates('published_date', 'year')
|
||||
years = self._qs_date_or_datetime(context['object_list'], 'year')
|
||||
if len(years) == 1:
|
||||
return self._format_months(context)
|
||||
else:
|
||||
return [
|
||||
(("?year=%s" % year.strftime("%Y")), year.strftime("%Y"))
|
||||
for year in
|
||||
context['object_list'].dates('published_date', 'year')
|
||||
self._qs_date_or_datetime(context['object_list'], 'year')
|
||||
]
|
||||
|
||||
def _format_months(self, context):
|
||||
|
|
@ -310,7 +310,7 @@ class ModelListView(AdminModel2Mixin, generic.ListView):
|
|||
),
|
||||
date.strftime("%B %Y")
|
||||
) for date in
|
||||
context["object_list"].dates('published_date', 'month')
|
||||
self._qs_date_or_datetime(context['object_list'], 'month')
|
||||
]
|
||||
|
||||
def _format_days(self, context):
|
||||
|
|
@ -323,9 +323,16 @@ class ModelListView(AdminModel2Mixin, generic.ListView):
|
|||
),
|
||||
date.strftime("%B %d")
|
||||
) for date in
|
||||
context["object_list"].dates('published_date', 'day')
|
||||
self._qs_date_or_datetime(context['object_list'], 'day')
|
||||
]
|
||||
|
||||
def _qs_date_or_datetime(self, object_list, type):
|
||||
if isinstance(self.model._meta.get_field(self.model_admin.date_hierarchy), models.DateTimeField):
|
||||
qs = object_list.datetimes(self.model_admin.date_hierarchy, type)
|
||||
else:
|
||||
qs = object_list.dates(self.model_admin.date_hierarchy, type)
|
||||
return qs
|
||||
|
||||
def get_success_url(self):
|
||||
view_name = 'admin2:{}_{}_index'.format(
|
||||
self.app_label, self.model_name)
|
||||
|
|
@ -511,7 +518,7 @@ class PasswordChangeView(Admin2Mixin, generic.UpdateView):
|
|||
default_template_name = 'auth/password_change_form.html'
|
||||
form_class = AdminPasswordChangeForm
|
||||
admin_form_class = PasswordChangeForm
|
||||
model = get_user_model()
|
||||
model = settings.AUTH_USER_MODEL
|
||||
success_url = reverse_lazy('admin2:password_change_done')
|
||||
|
||||
def get_form_kwargs(self, **kwargs):
|
||||
|
|
@ -529,6 +536,9 @@ class PasswordChangeView(Admin2Mixin, generic.UpdateView):
|
|||
return self.admin_form_class
|
||||
return super(PasswordChangeView, self).get_form_class()
|
||||
|
||||
def get_queryset(self):
|
||||
from django.contrib.auth import get_user_model
|
||||
return get_user_model()._default_manager.all()
|
||||
|
||||
class PasswordChangeDoneView(Admin2Mixin, generic.TemplateView):
|
||||
|
||||
|
|
|
|||
|
|
@ -7,13 +7,14 @@ from django.utils.encoding import python_2_unicode_compatible
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Post(models.Model):
|
||||
title = models.CharField(max_length=255, verbose_name=_('title'))
|
||||
body = models.TextField(verbose_name=_('body'))
|
||||
published = models.BooleanField(default=False, verbose_name=_('published'))
|
||||
published_date = models.DateField(blank=True, null=True)
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
class Meta:
|
||||
|
|
@ -21,11 +22,13 @@ class Post(models.Model):
|
|||
verbose_name_plural = _('posts')
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Comment(models.Model):
|
||||
post = models.ForeignKey(Post, verbose_name=_('post'), related_name="comments")
|
||||
post = models.ForeignKey(
|
||||
Post, verbose_name=_('post'), related_name="comments")
|
||||
body = models.TextField(verbose_name=_('body'))
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.body
|
||||
|
||||
class Meta:
|
||||
|
|
@ -33,7 +36,7 @@ class Comment(models.Model):
|
|||
verbose_name_plural = _('comments')
|
||||
|
||||
|
||||
#### Models needed for testing NestedObjects
|
||||
# Models needed for testing NestedObjects
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Count(models.Model):
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
# make sure that everything is setup for tests. Django 1.6 doesn't necessarily
|
||||
# load the urls.py before the tests are run.
|
||||
import example.urls
|
||||
|
||||
from test_apiviews import *
|
||||
from test_builtin_api_resources import *
|
||||
from test_permissions import *
|
||||
from test_modelforms import *
|
||||
from test_views import *
|
||||
from test_nestedobjects import *
|
||||
from test_filters import *
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
from __future__ import unicode_literals
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
from django.utils import simplejson as json
|
||||
from django.utils.encoding import force_text
|
||||
import json
|
||||
|
||||
|
||||
from djadmin2 import apiviews
|
||||
|
|
@ -69,7 +71,7 @@ class ListCreateAPIViewTest(APITestCase):
|
|||
response.render()
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn('"__str__": "Foo"', response.content)
|
||||
self.assertIn('"__unicode__": "Foo"', force_text(response.content))
|
||||
|
||||
def test_pagination(self):
|
||||
request = self.factory.get(reverse('admin2:blog_post_api_list'))
|
||||
|
|
@ -81,7 +83,7 @@ class ListCreateAPIViewTest(APITestCase):
|
|||
response.render()
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
data = json.loads(response.content)
|
||||
data = json.loads(force_text(response.content))
|
||||
self.assertEqual(data['count'], 0)
|
||||
# next and previous fields exist, but are null because we have no
|
||||
# content
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
# vim:fenc=utf-8
|
||||
|
||||
from django.test import TestCase
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.test.client import RequestFactory
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
|
|
@ -13,33 +12,30 @@ import djadmin2.filters as djadmin2_filters
|
|||
|
||||
import django_filters
|
||||
|
||||
|
||||
class PostAdminSimple(djadmin2.ModelAdmin2):
|
||||
list_filter = ['published', ]
|
||||
|
||||
|
||||
class PostAdminWithFilterInstances(djadmin2.ModelAdmin2):
|
||||
list_filter = [
|
||||
django_filters.BooleanFilter(name='published'),
|
||||
]
|
||||
|
||||
|
||||
class FS(django_filters.FilterSet):
|
||||
class Meta:
|
||||
model = Post
|
||||
fields = ['published']
|
||||
|
||||
|
||||
class PostAdminWithFilterSetInst(djadmin2.ModelAdmin2):
|
||||
list_filter = FS
|
||||
|
||||
|
||||
class ListFilterBuilderTest(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.rf = RequestFactory()
|
||||
|
||||
def test_filter_building(self):
|
||||
class PostAdminSimple(djadmin2.ModelAdmin2):
|
||||
list_filter = ['published', ]
|
||||
|
||||
|
||||
class PostAdminWithFilterInstances(djadmin2.ModelAdmin2):
|
||||
list_filter = [
|
||||
django_filters.BooleanFilter(name='published'),
|
||||
]
|
||||
|
||||
class FS(django_filters.FilterSet):
|
||||
class Meta:
|
||||
model = Post
|
||||
fields = ['published']
|
||||
|
||||
|
||||
class PostAdminWithFilterSetInst(djadmin2.ModelAdmin2):
|
||||
list_filter = FS
|
||||
|
||||
Post.objects.create(title="post_1_title", body="body")
|
||||
Post.objects.create(title="post_2_title", body="another body")
|
||||
request = self.rf.get(reverse("admin2:dashboard"))
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from django import forms
|
||||
from django.test import TestCase
|
||||
|
||||
|
|
@ -8,6 +10,7 @@ from ..models import Post
|
|||
|
||||
|
||||
class ModelFormFactoryTest(TestCase):
|
||||
|
||||
def test_modelform_factory(self):
|
||||
form_class = modelform_factory(Post)
|
||||
self.assertTrue(form_class)
|
||||
|
|
@ -16,8 +19,9 @@ class ModelFormFactoryTest(TestCase):
|
|||
|
||||
|
||||
class GetFloppyformWidgetTest(TestCase):
|
||||
|
||||
def assertExpectWidget(self, instance, new_class_,
|
||||
equal_attributes=None, new_attributes=None):
|
||||
equal_attributes=None, new_attributes=None):
|
||||
new_instance = floppify_widget(instance)
|
||||
self.assertEqual(new_instance.__class__, new_class_)
|
||||
if equal_attributes:
|
||||
|
|
@ -33,8 +37,8 @@ class GetFloppyformWidgetTest(TestCase):
|
|||
old_attr = getattr(instance, attribute)
|
||||
new_attr = getattr(new_instance, attribute)
|
||||
self.assertEqual(old_attr, new_attr,
|
||||
'Original widget\'s attribute was not copied: %r != %r' %
|
||||
(old_attr, new_attr))
|
||||
'Original widget\'s attribute was not copied: %r != %r' %
|
||||
(old_attr, new_attr))
|
||||
if new_attributes:
|
||||
for attribute, value in new_attributes.items():
|
||||
self.assertTrue(
|
||||
|
|
@ -43,8 +47,8 @@ class GetFloppyformWidgetTest(TestCase):
|
|||
'generated widget %r' % (attribute, new_instance))
|
||||
new_attr = getattr(new_instance, attribute)
|
||||
self.assertEqual(new_attr, value,
|
||||
'Generated widget\'s attribute is not as expected: '
|
||||
'%r != %r' % (new_attr, value))
|
||||
'Generated widget\'s attribute is not as expected: '
|
||||
'%r != %r' % (new_attr, value))
|
||||
|
||||
def test_created_widget_doesnt_leak_attributes_into_original_widget(self):
|
||||
widget = forms.TextInput()
|
||||
|
|
@ -146,7 +150,7 @@ class GetFloppyformWidgetTest(TestCase):
|
|||
widget,
|
||||
floppyforms.widgets.ClearableFileInput,
|
||||
['initial_text', 'input_text', 'clear_checkbox_label',
|
||||
'template_with_initial', 'template_with_clear'])
|
||||
'template_with_initial', 'template_with_clear'])
|
||||
|
||||
def test_textarea_widget(self):
|
||||
self.assertExpectWidget(
|
||||
|
|
@ -222,7 +226,7 @@ class GetFloppyformWidgetTest(TestCase):
|
|||
forms.widgets.NullBooleanSelect(),
|
||||
floppyforms.widgets.NullBooleanSelect,
|
||||
('choices', 'allow_multiple_selected',))
|
||||
|
||||
|
||||
widget = forms.widgets.NullBooleanSelect()
|
||||
widget.choices = list(widget.choices)
|
||||
|
||||
|
|
@ -341,15 +345,15 @@ class GetFloppyformWidgetTest(TestCase):
|
|||
|
||||
widget = forms.extras.widgets.SelectDateWidget(
|
||||
attrs={'attribute': 'value'},
|
||||
years=[2010, 2011, 2012, 2013],
|
||||
required=False)
|
||||
years=[2010, 2011, 2012, 2013])
|
||||
self.assertExpectWidget(
|
||||
widget,
|
||||
floppyforms.widgets.SelectDateWidget,
|
||||
('attrs', 'years', 'required'))
|
||||
('attrs', 'years'))
|
||||
|
||||
|
||||
class ModelFormTest(TestCase):
|
||||
|
||||
def test_custom_base_form(self):
|
||||
class MyForm(forms.ModelForm):
|
||||
pass
|
||||
|
|
@ -411,6 +415,7 @@ class ModelFormTest(TestCase):
|
|||
|
||||
|
||||
class FieldWidgetTest(TestCase):
|
||||
|
||||
def test_dont_overwrite_none_default_widget(self):
|
||||
# we don't create the floppyform EmailInput for the email field here
|
||||
# since we have overwritten the default widget. However we replace the
|
||||
|
|
|
|||
|
|
@ -1,18 +1,22 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
from datetime import datetime
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Group
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test import TestCase, Client
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
from ..models import Post, Comment
|
||||
|
||||
|
||||
class BaseIntegrationTest(TestCase):
|
||||
|
||||
"""
|
||||
Base TestCase for integration tests.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
self.user = get_user_model()(username='user', is_staff=True,
|
||||
|
|
@ -23,12 +27,14 @@ class BaseIntegrationTest(TestCase):
|
|||
|
||||
|
||||
class AdminIndexTest(BaseIntegrationTest):
|
||||
|
||||
def test_view_ok(self):
|
||||
response = self.client.get(reverse("admin2:dashboard"))
|
||||
self.assertContains(response, reverse("admin2:blog_post_index"))
|
||||
|
||||
|
||||
class UserListTest(BaseIntegrationTest):
|
||||
|
||||
def test_search_users_m2m_group(self):
|
||||
# This test should cause the distinct search path to exectue
|
||||
group = Group.objects.create(name="Test Group")
|
||||
|
|
@ -40,6 +46,7 @@ class UserListTest(BaseIntegrationTest):
|
|||
|
||||
|
||||
class CommentListTest(BaseIntegrationTest):
|
||||
|
||||
def test_search_comments(self):
|
||||
# Test search across Foriegn Keys
|
||||
post_1 = Post.objects.create(title="post_1_title", body="body")
|
||||
|
|
@ -49,7 +56,8 @@ class CommentListTest(BaseIntegrationTest):
|
|||
Comment.objects.create(body="comment_post_2", post=post_2)
|
||||
|
||||
params = {"q": "post_1_title"}
|
||||
response = self.client.get(reverse("admin2:blog_comment_index"), params)
|
||||
response = self.client.get(
|
||||
reverse("admin2:blog_comment_index"), params)
|
||||
self.assertContains(response, "comment_post_1_a")
|
||||
self.assertContains(response, "comment_post_1_b")
|
||||
self.assertNotContains(response, "comment_post_2")
|
||||
|
|
@ -62,6 +70,7 @@ class CommentListTest(BaseIntegrationTest):
|
|||
|
||||
|
||||
class PostListTest(BaseIntegrationTest):
|
||||
|
||||
def _create_posts(self):
|
||||
Post.objects.bulk_create([
|
||||
Post(
|
||||
|
|
@ -129,7 +138,8 @@ class PostListTest(BaseIntegrationTest):
|
|||
|
||||
def test_actions_displayed(self):
|
||||
response = self.client.get(reverse("admin2:blog_post_index"))
|
||||
self.assertInHTML('<a tabindex="-1" href="#" data-name="action" data-value="DeleteSelectedAction">Delete selected items</a>', response.content)
|
||||
self.assertInHTML(
|
||||
'<a tabindex="-1" href="#" data-name="action" data-value="DeleteSelectedAction">Delete selected items</a>', force_text(response.content))
|
||||
|
||||
def test_actions_displayed_twice(self):
|
||||
# If actions_on_top and actions_on_bottom are both set
|
||||
|
|
@ -139,27 +149,33 @@ class PostListTest(BaseIntegrationTest):
|
|||
|
||||
def test_delete_selected_post(self):
|
||||
post = Post.objects.create(title="A Post Title", body="body")
|
||||
params = {'action': 'DeleteSelectedAction', 'selected_model_pk': str(post.pk)}
|
||||
params = {'action': 'DeleteSelectedAction',
|
||||
'selected_model_pk': str(post.pk)}
|
||||
response = self.client.post(reverse("admin2:blog_post_index"), params)
|
||||
# caution : uses pluralization
|
||||
self.assertInHTML('<p>Are you sure you want to delete the selected post? The following item will be deleted:</p>', response.content)
|
||||
self.assertInHTML(
|
||||
'<p>Are you sure you want to delete the selected post? The following item will be deleted:</p>', force_text(response.content))
|
||||
|
||||
def test_delete_selected_post_confirmation(self):
|
||||
post = Post.objects.create(title="A Post Title", body="body")
|
||||
params = {'action': 'DeleteSelectedAction', 'selected_model_pk': str(post.pk), 'confirmed': 'yes'}
|
||||
params = {'action': 'DeleteSelectedAction',
|
||||
'selected_model_pk': str(post.pk), 'confirmed': 'yes'}
|
||||
response = self.client.post(reverse("admin2:blog_post_index"), params)
|
||||
self.assertRedirects(response, reverse("admin2:blog_post_index"))
|
||||
|
||||
def test_delete_selected_post_none_selected(self):
|
||||
Post.objects.create(title="A Post Title", body="body")
|
||||
params = {'action': 'DeleteSelectedAction'}
|
||||
response = self.client.post(reverse("admin2:blog_post_index"), params, follow=True)
|
||||
self.assertContains(response, "Items must be selected in order to perform actions on them. No items have been changed.")
|
||||
response = self.client.post(
|
||||
reverse("admin2:blog_post_index"), params, follow=True)
|
||||
self.assertContains(
|
||||
response, "Items must be selected in order to perform actions on them. No items have been changed.")
|
||||
|
||||
def test_search_posts(self):
|
||||
Post.objects.create(title="A Post Title", body="body")
|
||||
Post.objects.create(title="Another Post Title", body="body")
|
||||
Post.objects.create(title="Post With Keyword In Body", body="another post body")
|
||||
Post.objects.create(
|
||||
title="Post With Keyword In Body", body="another post body")
|
||||
params = {"q": "another"}
|
||||
response = self.client.get(reverse("admin2:blog_post_index"), params)
|
||||
self.assertContains(response, "Another Post Title")
|
||||
|
|
@ -167,12 +183,14 @@ class PostListTest(BaseIntegrationTest):
|
|||
self.assertNotContains(response, "A Post Title")
|
||||
|
||||
def test_renderer_title(self):
|
||||
Post.objects.create(title='a lowercase title', body='body', published=False)
|
||||
Post.objects.create(
|
||||
title='a lowercase title', body='body', published=False)
|
||||
response = self.client.get(reverse('admin2:blog_post_index'))
|
||||
self.assertContains(response, 'A Lowercase Title')
|
||||
|
||||
def test_renderer_body(self):
|
||||
Post.objects.create(title='title', body='a lowercase body', published=False)
|
||||
Post.objects.create(
|
||||
title='title', body='a lowercase body', published=False)
|
||||
response = self.client.get(reverse('admin2:blog_post_index'))
|
||||
self.assertContains(response, 'a lowercase body')
|
||||
|
||||
|
|
@ -311,7 +329,8 @@ class PostListTestCustomAction(BaseIntegrationTest):
|
|||
|
||||
def test_publish_action_displayed_in_list(self):
|
||||
response = self.client.get(reverse("admin2:blog_post_index"))
|
||||
self.assertInHTML('<a tabindex="-1" href="#" data-name="action" data-value="CustomPublishAction">Publish selected items</a>', response.content)
|
||||
self.assertInHTML(
|
||||
'<a tabindex="-1" href="#" data-name="action" data-value="CustomPublishAction">Publish selected items</a>', force_text(response.content))
|
||||
|
||||
def test_publish_selected_items(self):
|
||||
post = Post.objects.create(title="A Post Title",
|
||||
|
|
@ -329,7 +348,8 @@ class PostListTestCustomAction(BaseIntegrationTest):
|
|||
|
||||
def test_unpublish_action_displayed_in_list(self):
|
||||
response = self.client.get(reverse("admin2:blog_post_index"))
|
||||
self.assertInHTML('<a tabindex="-1" href="#" data-name="action" data-value="unpublish_items">Unpublish selected items</a>', response.content)
|
||||
self.assertInHTML(
|
||||
'<a tabindex="-1" href="#" data-name="action" data-value="unpublish_items">Unpublish selected items</a>', force_text(response.content))
|
||||
|
||||
def test_unpublish_selected_items(self):
|
||||
post = Post.objects.create(title="A Post Title",
|
||||
|
|
@ -346,6 +366,7 @@ class PostListTestCustomAction(BaseIntegrationTest):
|
|||
|
||||
|
||||
class PostDetailViewTest(BaseIntegrationTest):
|
||||
|
||||
def test_view_ok(self):
|
||||
post = Post.objects.create(title="A Post Title", body="body")
|
||||
response = self.client.get(reverse("admin2:blog_post_detail",
|
||||
|
|
@ -354,9 +375,11 @@ class PostDetailViewTest(BaseIntegrationTest):
|
|||
|
||||
|
||||
class PostCreateViewTest(BaseIntegrationTest):
|
||||
|
||||
def test_view_ok(self):
|
||||
response = self.client.get(reverse("admin2:blog_post_create"))
|
||||
self.assertNotIn('''enctype="multipart/form-data"''', response.content)
|
||||
self.assertNotIn(
|
||||
'''enctype="multipart/form-data"''', force_text(response.content))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_create_post(self):
|
||||
|
|
@ -422,6 +445,7 @@ class PostCreateViewTest(BaseIntegrationTest):
|
|||
|
||||
|
||||
class PostDeleteViewTest(BaseIntegrationTest):
|
||||
|
||||
def test_view_ok(self):
|
||||
post = Post.objects.create(title="A Post Title", body="body")
|
||||
response = self.client.get(reverse("admin2:blog_post_delete",
|
||||
|
|
@ -437,9 +461,11 @@ class PostDeleteViewTest(BaseIntegrationTest):
|
|||
|
||||
|
||||
class PostDeleteActionTest(BaseIntegrationTest):
|
||||
|
||||
"""
|
||||
Tests the behaviour of the 'Delete selected items' action.
|
||||
"""
|
||||
|
||||
def test_confirmation_page(self):
|
||||
p1 = Post.objects.create(title="A Post Title", body="body")
|
||||
p2 = Post.objects.create(title="A Post Title", body="body")
|
||||
|
|
|
|||
|
|
@ -130,6 +130,7 @@ INSTALLED_APPS = (
|
|||
'djadmin2.themes.djadmin2theme_default',
|
||||
'blog',
|
||||
'files',
|
||||
'polls'
|
||||
)
|
||||
|
||||
# A sample logging configuration. The only tangible logging
|
||||
|
|
@ -162,25 +163,4 @@ LOGGING = {
|
|||
}
|
||||
|
||||
|
||||
ADMIN2_THEME_DIRECTORY = "djadmin2theme_default"
|
||||
|
||||
|
||||
########## TOOLBAR CONFIGURATION
|
||||
# See: https://github.com/django-debug-toolbar/django-debug-toolbar#installation
|
||||
INSTALLED_APPS += (
|
||||
'debug_toolbar',
|
||||
)
|
||||
|
||||
# See: https://github.com/django-debug-toolbar/django-debug-toolbar#installation
|
||||
INTERNAL_IPS = ('127.0.0.1',)
|
||||
|
||||
# See: https://github.com/django-debug-toolbar/django-debug-toolbar#installation
|
||||
MIDDLEWARE_CLASSES += (
|
||||
'debug_toolbar.middleware.DebugToolbarMiddleware',
|
||||
)
|
||||
|
||||
DEBUG_TOOLBAR_CONFIG = {
|
||||
'INTERCEPT_REDIRECTS': False,
|
||||
'SHOW_TEMPLATE_CONTEXT': True,
|
||||
}
|
||||
########## END TOOLBAR CONFIGURATION
|
||||
ADMIN2_THEME_DIRECTORY = "djadmin2theme_default"
|
||||
|
|
@ -2,14 +2,17 @@
|
|||
from __future__ import division, absolute_import, unicode_literals
|
||||
|
||||
from django.db import models
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class CaptionedFile(models.Model):
|
||||
caption = models.CharField(max_length=200, verbose_name=_('caption'))
|
||||
publication = models.FileField(upload_to='media', verbose_name=_('Uploaded File'))
|
||||
publication = models.FileField(
|
||||
upload_to='media', verbose_name=_('Uploaded File'))
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.caption
|
||||
|
||||
class Meta:
|
||||
|
|
@ -17,11 +20,13 @@ class CaptionedFile(models.Model):
|
|||
verbose_name_plural = _('Captioned Files')
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class UncaptionedFile(models.Model):
|
||||
publication = models.FileField(upload_to='media', verbose_name=_('Uploaded File'))
|
||||
publication = models.FileField(
|
||||
upload_to='media', verbose_name=_('Uploaded File'))
|
||||
|
||||
def __unicode__(self):
|
||||
return unicode(self.publication)
|
||||
def __str__(self):
|
||||
return self.publication
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Uncaptioned File')
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
from test_models import *
|
||||
from test_views import *
|
||||
|
|
@ -2,6 +2,7 @@ from django.contrib.auth import get_user_model
|
|||
from django.core.urlresolvers import reverse
|
||||
from django.test import TestCase, Client
|
||||
from django.utils import timezone
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
from ..models import CaptionedFile
|
||||
|
||||
|
|
@ -12,9 +13,11 @@ fixture_file = path.join(fixture_dir, 'pubtest.txt')
|
|||
|
||||
|
||||
class BaseIntegrationTest(TestCase):
|
||||
|
||||
"""
|
||||
Base TestCase for integration tests.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
self.user = get_user_model()(username='user', is_staff=True,
|
||||
|
|
@ -25,55 +28,77 @@ class BaseIntegrationTest(TestCase):
|
|||
|
||||
|
||||
class AdminIndexTest(BaseIntegrationTest):
|
||||
|
||||
def test_view_ok(self):
|
||||
response = self.client.get(reverse("admin2:dashboard"))
|
||||
self.assertContains(response, reverse("admin2:files_captionedfile_index"))
|
||||
self.assertContains(
|
||||
response, reverse("admin2:files_captionedfile_index"))
|
||||
|
||||
|
||||
class CaptionedFileListTest(BaseIntegrationTest):
|
||||
|
||||
def test_view_ok(self):
|
||||
captioned_file = CaptionedFile.objects.create(caption="some file", publication=fixture_file)
|
||||
captioned_file = CaptionedFile.objects.create(
|
||||
caption="some file", publication=fixture_file)
|
||||
response = self.client.get(reverse("admin2:files_captionedfile_index"))
|
||||
self.assertContains(response, captioned_file.caption)
|
||||
|
||||
def test_actions_displayed(self):
|
||||
response = self.client.get(reverse("admin2:files_captionedfile_index"))
|
||||
self.assertInHTML('<a tabindex="-1" href="#" data-name="action" data-value="DeleteSelectedAction">Delete selected items</a>', response.content)
|
||||
self.assertInHTML(
|
||||
'<a tabindex="-1" href="#" data-name="action" data-value="DeleteSelectedAction">Delete selected items</a>', force_text(response.content))
|
||||
|
||||
def test_delete_selected_captioned_file(self):
|
||||
captioned_file = CaptionedFile.objects.create(caption="some file", publication=fixture_file)
|
||||
params = {'action': 'DeleteSelectedAction', 'selected_model_pk': str(captioned_file.pk)}
|
||||
response = self.client.post(reverse("admin2:files_captionedfile_index"), params)
|
||||
self.assertInHTML('<p>Are you sure you want to delete the selected Captioned File? The following item will be deleted:</p>', response.content)
|
||||
captioned_file = CaptionedFile.objects.create(
|
||||
caption="some file", publication=fixture_file)
|
||||
params = {'action': 'DeleteSelectedAction',
|
||||
'selected_model_pk': str(captioned_file.pk)}
|
||||
response = self.client.post(
|
||||
reverse("admin2:files_captionedfile_index"), params)
|
||||
self.assertInHTML(
|
||||
'<p>Are you sure you want to delete the selected Captioned File? The following item will be deleted:</p>', force_text(response.content))
|
||||
|
||||
def test_delete_selected_captioned_file_confirmation(self):
|
||||
captioned_file = CaptionedFile.objects.create(caption="some file", publication=fixture_file)
|
||||
params = {'action': 'DeleteSelectedAction', 'selected_model_pk': str(captioned_file.pk), 'confirmed': 'yes'}
|
||||
response = self.client.post(reverse("admin2:files_captionedfile_index"), params)
|
||||
self.assertRedirects(response, reverse("admin2:files_captionedfile_index"))
|
||||
captioned_file = CaptionedFile.objects.create(
|
||||
caption="some file", publication=fixture_file)
|
||||
params = {'action': 'DeleteSelectedAction', 'selected_model_pk': str(
|
||||
captioned_file.pk), 'confirmed': 'yes'}
|
||||
response = self.client.post(
|
||||
reverse("admin2:files_captionedfile_index"), params)
|
||||
self.assertRedirects(
|
||||
response, reverse("admin2:files_captionedfile_index"))
|
||||
|
||||
def test_delete_selected_captioned_file_none_selected(self):
|
||||
CaptionedFile.objects.create(caption="some file", publication=fixture_file)
|
||||
CaptionedFile.objects.create(
|
||||
caption="some file", publication=fixture_file)
|
||||
params = {'action': 'DeleteSelectedAction'}
|
||||
response = self.client.post(reverse("admin2:files_captionedfile_index"), params, follow=True)
|
||||
self.assertContains(response, "Items must be selected in order to perform actions on them. No items have been changed.")
|
||||
response = self.client.post(
|
||||
reverse("admin2:files_captionedfile_index"), params, follow=True)
|
||||
self.assertContains(
|
||||
response, "Items must be selected in order to perform actions on them. No items have been changed.")
|
||||
|
||||
|
||||
class CaptionedFileDetailViewTest(BaseIntegrationTest):
|
||||
|
||||
def test_view_ok(self):
|
||||
captioned_file = CaptionedFile.objects.create(caption="some file", publication=fixture_file)
|
||||
response = self.client.get(reverse("admin2:files_captionedfile_detail", args=(captioned_file.pk, )))
|
||||
captioned_file = CaptionedFile.objects.create(
|
||||
caption="some file", publication=fixture_file)
|
||||
response = self.client.get(
|
||||
reverse("admin2:files_captionedfile_detail", args=(captioned_file.pk, )))
|
||||
self.assertContains(response, captioned_file.caption)
|
||||
|
||||
|
||||
class CaptionedFileCreateViewTest(BaseIntegrationTest):
|
||||
|
||||
def test_view_ok(self):
|
||||
response = self.client.get(reverse("admin2:files_captionedfile_create"))
|
||||
self.assertIn('''enctype="multipart/form-data"''', response.content)
|
||||
response = self.client.get(
|
||||
reverse("admin2:files_captionedfile_create"))
|
||||
self.assertIn(
|
||||
'enctype="multipart/form-data"', force_text(response.content))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_create_captioned_file(self):
|
||||
with open(fixture_file, 'r') as fp:
|
||||
with open(fixture_file, 'rb') as fp:
|
||||
params = {
|
||||
"caption": "some file",
|
||||
"publication": fp,
|
||||
|
|
@ -81,15 +106,17 @@ class CaptionedFileCreateViewTest(BaseIntegrationTest):
|
|||
response = self.client.post(reverse("admin2:files_captionedfile_create"),
|
||||
params,
|
||||
follow=True)
|
||||
self.assertTrue(CaptionedFile.objects.filter(caption="some file").exists())
|
||||
self.assertRedirects(response, reverse("admin2:files_captionedfile_index"))
|
||||
self.assertTrue(
|
||||
CaptionedFile.objects.filter(caption="some file").exists())
|
||||
self.assertRedirects(
|
||||
response, reverse("admin2:files_captionedfile_index"))
|
||||
|
||||
def test_save_and_add_another_redirects_to_create(self):
|
||||
"""
|
||||
Tests that choosing 'Save and add another' from the model create
|
||||
page redirects the user to the model create page.
|
||||
"""
|
||||
with open(fixture_file, 'r') as fp:
|
||||
with open(fixture_file, 'rb') as fp:
|
||||
params = {
|
||||
"caption": "some file",
|
||||
"publication": fp,
|
||||
|
|
@ -97,15 +124,17 @@ class CaptionedFileCreateViewTest(BaseIntegrationTest):
|
|||
}
|
||||
response = self.client.post(reverse("admin2:files_captionedfile_create"),
|
||||
params)
|
||||
self.assertTrue(CaptionedFile.objects.filter(caption="some file").exists())
|
||||
self.assertRedirects(response, reverse("admin2:files_captionedfile_create"))
|
||||
self.assertTrue(
|
||||
CaptionedFile.objects.filter(caption="some file").exists())
|
||||
self.assertRedirects(
|
||||
response, reverse("admin2:files_captionedfile_create"))
|
||||
|
||||
def test_save_and_continue_editing_redirects_to_update(self):
|
||||
"""
|
||||
Tests that choosing "Save and continue editing" redirects
|
||||
the user to the model update form.
|
||||
"""
|
||||
with open(fixture_file, 'r') as fp:
|
||||
with open(fixture_file, 'rb') as fp:
|
||||
params = {
|
||||
"caption": "some file",
|
||||
"publication": fp,
|
||||
|
|
@ -119,27 +148,36 @@ class CaptionedFileCreateViewTest(BaseIntegrationTest):
|
|||
|
||||
|
||||
class CaptionedFileDeleteViewTest(BaseIntegrationTest):
|
||||
|
||||
def test_view_ok(self):
|
||||
captioned_file = CaptionedFile.objects.create(caption="some file", publication=fixture_file)
|
||||
captioned_file = CaptionedFile.objects.create(
|
||||
caption="some file", publication=fixture_file)
|
||||
response = self.client.get(reverse("admin2:files_captionedfile_delete",
|
||||
args=(captioned_file.pk, )))
|
||||
self.assertContains(response, captioned_file.caption)
|
||||
|
||||
def test_delete_captioned_file(self):
|
||||
captioned_file = CaptionedFile.objects.create(caption="some file", publication=fixture_file)
|
||||
captioned_file = CaptionedFile.objects.create(
|
||||
caption="some file", publication=fixture_file)
|
||||
response = self.client.post(reverse("admin2:files_captionedfile_delete",
|
||||
args=(captioned_file.pk, )))
|
||||
self.assertRedirects(response, reverse("admin2:files_captionedfile_index"))
|
||||
self.assertFalse(CaptionedFile.objects.filter(pk=captioned_file.pk).exists())
|
||||
self.assertRedirects(
|
||||
response, reverse("admin2:files_captionedfile_index"))
|
||||
self.assertFalse(
|
||||
CaptionedFile.objects.filter(pk=captioned_file.pk).exists())
|
||||
|
||||
|
||||
class FileDeleteActionTest(BaseIntegrationTest):
|
||||
|
||||
"""
|
||||
Tests the behaviour of the 'Delete selected items' action.
|
||||
"""
|
||||
|
||||
def test_confirmation_page(self):
|
||||
cf1 = captioned_file = CaptionedFile.objects.create(caption="some file", publication=fixture_file)
|
||||
cf2 = captioned_file = CaptionedFile.objects.create(caption="some file", publication=fixture_file)
|
||||
cf1 = CaptionedFile.objects.create(
|
||||
caption="some file", publication=fixture_file)
|
||||
cf2 = CaptionedFile.objects.create(
|
||||
caption="some file", publication=fixture_file)
|
||||
params = {
|
||||
'action': 'DeleteSelectedAction',
|
||||
'selected_model_pk': [cf1.pk, cf2.pk]
|
||||
|
|
@ -150,8 +188,10 @@ class FileDeleteActionTest(BaseIntegrationTest):
|
|||
self.assertContains(response, cf2.caption)
|
||||
|
||||
def test_results_page(self):
|
||||
cf1 = captioned_file = CaptionedFile.objects.create(caption="some file", publication=fixture_file)
|
||||
cf2 = captioned_file = CaptionedFile.objects.create(caption="some file", publication=fixture_file)
|
||||
cf1 = CaptionedFile.objects.create(
|
||||
caption="some file", publication=fixture_file)
|
||||
cf2 = CaptionedFile.objects.create(
|
||||
caption="some file", publication=fixture_file)
|
||||
params = {
|
||||
'action': 'DeleteSelectedAction',
|
||||
'selected_model_pk': [cf1.pk, cf2.pk],
|
||||
|
|
|
|||
|
|
@ -3,16 +3,18 @@ from __future__ import division, absolute_import, unicode_literals
|
|||
|
||||
import datetime
|
||||
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Poll(models.Model):
|
||||
question = models.CharField(max_length=200, verbose_name=_('question'))
|
||||
pub_date = models.DateTimeField(verbose_name=_('date published'))
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.question
|
||||
|
||||
def was_published_recently(self):
|
||||
|
|
@ -26,12 +28,14 @@ class Poll(models.Model):
|
|||
verbose_name_plural = _('polls')
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Choice(models.Model):
|
||||
poll = models.ForeignKey(Poll, verbose_name=_('poll'))
|
||||
choice_text = models.CharField(max_length=200, verbose_name=_('choice text'))
|
||||
choice_text = models.CharField(
|
||||
max_length=200, verbose_name=_('choice text'))
|
||||
votes = models.IntegerField(default=0, verbose_name=_('votes'))
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.choice_text
|
||||
|
||||
class Meta:
|
||||
|
|
@ -2,14 +2,17 @@ from django.contrib.auth import get_user_model
|
|||
from django.core.urlresolvers import reverse
|
||||
from django.test import TestCase, Client
|
||||
from django.utils import timezone
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
from ..models import Poll
|
||||
|
||||
|
||||
class BaseIntegrationTest(TestCase):
|
||||
|
||||
"""
|
||||
Base TestCase for integration tests.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
self.user = get_user_model()(username='user', is_staff=True,
|
||||
|
|
@ -20,48 +23,63 @@ class BaseIntegrationTest(TestCase):
|
|||
|
||||
|
||||
class AdminIndexTest(BaseIntegrationTest):
|
||||
|
||||
def test_view_ok(self):
|
||||
response = self.client.get(reverse("admin2:dashboard"))
|
||||
self.assertContains(response, reverse("admin2:polls_poll_index"))
|
||||
|
||||
|
||||
class PollListTest(BaseIntegrationTest):
|
||||
|
||||
def test_view_ok(self):
|
||||
poll = Poll.objects.create(question="some question", pub_date=timezone.now())
|
||||
poll = Poll.objects.create(
|
||||
question="some question", pub_date=timezone.now())
|
||||
response = self.client.get(reverse("admin2:polls_poll_index"))
|
||||
self.assertContains(response, poll.question)
|
||||
|
||||
def test_actions_displayed(self):
|
||||
response = self.client.get(reverse("admin2:polls_poll_index"))
|
||||
self.assertInHTML('<a tabindex="-1" href="#" data-name="action" data-value="DeleteSelectedAction">Delete selected items</a>', response.content)
|
||||
self.assertInHTML(
|
||||
'<a tabindex="-1" href="#" data-name="action" data-value="DeleteSelectedAction">Delete selected items</a>', force_text(response.content))
|
||||
|
||||
def test_delete_selected_poll(self):
|
||||
poll = Poll.objects.create(question="some question", pub_date=timezone.now())
|
||||
params = {'action': 'DeleteSelectedAction', 'selected_model_pk': str(poll.pk)}
|
||||
poll = Poll.objects.create(
|
||||
question="some question", pub_date=timezone.now())
|
||||
params = {'action': 'DeleteSelectedAction',
|
||||
'selected_model_pk': str(poll.pk)}
|
||||
response = self.client.post(reverse("admin2:polls_poll_index"), params)
|
||||
self.assertInHTML('<p>Are you sure you want to delete the selected poll? All of the following items will be deleted:</p>', response.content)
|
||||
self.assertInHTML(
|
||||
'<p>Are you sure you want to delete the selected poll? The following item will be deleted:</p>', force_text(response.content))
|
||||
|
||||
def test_delete_selected_poll_confirmation(self):
|
||||
poll = Poll.objects.create(question="some question", pub_date=timezone.now())
|
||||
params = {'action': 'DeleteSelectedAction', 'selected_model_pk': str(poll.pk), 'confirmed': 'yes'}
|
||||
poll = Poll.objects.create(
|
||||
question="some question", pub_date=timezone.now())
|
||||
params = {'action': 'DeleteSelectedAction',
|
||||
'selected_model_pk': str(poll.pk), 'confirmed': 'yes'}
|
||||
response = self.client.post(reverse("admin2:polls_poll_index"), params)
|
||||
self.assertRedirects(response, reverse("admin2:polls_poll_index"))
|
||||
|
||||
def test_delete_selected_poll_none_selected(self):
|
||||
Poll.objects.create(question="some question", pub_date=timezone.now())
|
||||
params = {'action': 'DeleteSelectedAction'}
|
||||
response = self.client.post(reverse("admin2:polls_poll_index"), params, follow=True)
|
||||
self.assertContains(response, "Items must be selected in order to perform actions on them. No items have been changed.")
|
||||
response = self.client.post(
|
||||
reverse("admin2:polls_poll_index"), params, follow=True)
|
||||
self.assertContains(
|
||||
response, "Items must be selected in order to perform actions on them. No items have been changed.")
|
||||
|
||||
|
||||
class PollDetailViewTest(BaseIntegrationTest):
|
||||
|
||||
def test_view_ok(self):
|
||||
poll = Poll.objects.create(question="some question", pub_date=timezone.now())
|
||||
response = self.client.get(reverse("admin2:polls_poll_detail", args=(poll.pk, )))
|
||||
poll = Poll.objects.create(
|
||||
question="some question", pub_date=timezone.now())
|
||||
response = self.client.get(
|
||||
reverse("admin2:polls_poll_detail", args=(poll.pk, )))
|
||||
self.assertContains(response, poll.question)
|
||||
|
||||
|
||||
class PollCreateViewTest(BaseIntegrationTest):
|
||||
|
||||
def test_view_ok(self):
|
||||
response = self.client.get(reverse("admin2:polls_poll_create"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
|
@ -119,14 +137,17 @@ class PollCreateViewTest(BaseIntegrationTest):
|
|||
|
||||
|
||||
class PollDeleteViewTest(BaseIntegrationTest):
|
||||
|
||||
def test_view_ok(self):
|
||||
poll = Poll.objects.create(question="some question", pub_date=timezone.now())
|
||||
poll = Poll.objects.create(
|
||||
question="some question", pub_date=timezone.now())
|
||||
response = self.client.get(reverse("admin2:polls_poll_delete",
|
||||
args=(poll.pk, )))
|
||||
self.assertContains(response, poll.question)
|
||||
|
||||
def test_delete_poll(self):
|
||||
poll = Poll.objects.create(question="some question", pub_date=timezone.now())
|
||||
poll = Poll.objects.create(
|
||||
question="some question", pub_date=timezone.now())
|
||||
response = self.client.post(reverse("admin2:polls_poll_delete",
|
||||
args=(poll.pk, )))
|
||||
self.assertRedirects(response, reverse("admin2:polls_poll_index"))
|
||||
|
|
@ -134,12 +155,16 @@ class PollDeleteViewTest(BaseIntegrationTest):
|
|||
|
||||
|
||||
class PollDeleteActionTest(BaseIntegrationTest):
|
||||
|
||||
"""
|
||||
Tests the behaviour of the 'Delete selected items' action.
|
||||
"""
|
||||
|
||||
def test_confirmation_page(self):
|
||||
p1 = Poll.objects.create(question="some question", pub_date=timezone.now())
|
||||
p2 = Poll.objects.create(question="some question", pub_date=timezone.now())
|
||||
p1 = Poll.objects.create(
|
||||
question="some question", pub_date=timezone.now())
|
||||
p2 = Poll.objects.create(
|
||||
question="some question", pub_date=timezone.now())
|
||||
params = {
|
||||
'action': 'DeleteSelectedAction',
|
||||
'selected_model_pk': [p1.pk, p2.pk]
|
||||
|
|
@ -150,8 +175,10 @@ class PollDeleteActionTest(BaseIntegrationTest):
|
|||
self.assertContains(response, p2.question)
|
||||
|
||||
def test_results_page(self):
|
||||
p1 = Poll.objects.create(question="some question", pub_date=timezone.now())
|
||||
p2 = Poll.objects.create(question="some question", pub_date=timezone.now())
|
||||
p1 = Poll.objects.create(
|
||||
question="some question", pub_date=timezone.now())
|
||||
p2 = Poll.objects.create(
|
||||
question="some question", pub_date=timezone.now())
|
||||
params = {
|
||||
'action': 'DeleteSelectedAction',
|
||||
'selected_model_pk': [p1.pk, p2.pk],
|
||||
|
|
@ -1,188 +0,0 @@
|
|||
# Django settings for example2 project.
|
||||
|
||||
DEBUG = True
|
||||
TEMPLATE_DEBUG = DEBUG
|
||||
|
||||
ADMINS = (
|
||||
# ('Your Name', 'your_email@example.com'),
|
||||
)
|
||||
|
||||
MANAGERS = ADMINS
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': 'example2.db',
|
||||
}
|
||||
}
|
||||
|
||||
# Hosts/domain names that are valid for this site; required if DEBUG is False
|
||||
# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
# Local time zone for this installation. Choices can be found here:
|
||||
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
|
||||
# although not all choices may be available on all operating systems.
|
||||
# In a Windows environment this must be set to your system time zone.
|
||||
TIME_ZONE = 'America/Chicago'
|
||||
|
||||
# Language code for this installation. All choices can be found here:
|
||||
# http://www.i18nguy.com/unicode/language-identifiers.html
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
# If you set this to False, Django will make some optimizations so as not
|
||||
# to load the internationalization machinery.
|
||||
USE_I18N = True
|
||||
|
||||
# If you set this to False, Django will not format dates, numbers and
|
||||
# calendars according to the current locale.
|
||||
USE_L10N = True
|
||||
|
||||
# If you set this to False, Django will not use timezone-aware datetimes.
|
||||
USE_TZ = True
|
||||
|
||||
# Absolute filesystem path to the directory that will hold user-uploaded files.
|
||||
# Example: "/var/www/example.com/media/"
|
||||
MEDIA_ROOT = ''
|
||||
|
||||
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
||||
# trailing slash.
|
||||
# Examples: "http://example.com/media/", "http://media.example.com/"
|
||||
MEDIA_URL = ''
|
||||
|
||||
# Absolute path to the directory static files should be collected to.
|
||||
# Don't put anything in this directory yourself; store your static files
|
||||
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
|
||||
# Example: "/var/www/example.com/static/"
|
||||
STATIC_ROOT = ''
|
||||
|
||||
# URL prefix for static files.
|
||||
# Example: "http://example.com/static/", "http://static.example.com/"
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
# Additional locations of static files
|
||||
STATICFILES_DIRS = (
|
||||
# Put strings here, like "/home/html/static" or "C:/www/django/static".
|
||||
# Always use forward slashes, even on Windows.
|
||||
# Don't forget to use absolute paths, not relative paths.
|
||||
)
|
||||
|
||||
# List of finder classes that know how to find static files in
|
||||
# various locations.
|
||||
STATICFILES_FINDERS = (
|
||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
|
||||
)
|
||||
|
||||
# Make this unique, and don't share it with anybody.
|
||||
SECRET_KEY = 'vid$84s%19vhcss+(n$*pbc=nad2oab@^2s532_iesz2f6q=(z'
|
||||
|
||||
# List of callables that know how to import templates from various sources.
|
||||
TEMPLATE_LOADERS = (
|
||||
'django.template.loaders.filesystem.Loader',
|
||||
'django.template.loaders.app_directories.Loader',
|
||||
# 'django.template.loaders.eggs.Loader',
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
# Uncomment the next line for simple clickjacking protection:
|
||||
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'example2.urls'
|
||||
|
||||
# Python dotted path to the WSGI application used by Django's runserver.
|
||||
WSGI_APPLICATION = 'example2.wsgi.application'
|
||||
|
||||
TEMPLATE_DIRS = (
|
||||
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
|
||||
# Always use forward slashes, even on Windows.
|
||||
# Don't forget to use absolute paths, not relative paths.
|
||||
)
|
||||
|
||||
INSTALLED_APPS = (
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.sites',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django.contrib.admin',
|
||||
# Uncomment the next line to enable admin documentation:
|
||||
# 'django.contrib.admindocs',
|
||||
'floppyforms',
|
||||
'rest_framework',
|
||||
'djadmin2',
|
||||
'djadmin2.themes.djadmin2theme_default',
|
||||
'crispy_forms',
|
||||
'polls',
|
||||
)
|
||||
|
||||
try:
|
||||
import django_extensions
|
||||
INSTALLED_APPS += (
|
||||
'django_extensions',
|
||||
)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# A sample logging configuration. The only tangible logging
|
||||
# performed by this configuration is to send an email to
|
||||
# the site admins on every HTTP 500 error when DEBUG=False.
|
||||
# See http://docs.djangoproject.com/en/dev/topics/logging for
|
||||
# more details on how to customize your logging configuration.
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'filters': {
|
||||
'require_debug_false': {
|
||||
'()': 'django.utils.log.RequireDebugFalse'
|
||||
}
|
||||
},
|
||||
'handlers': {
|
||||
'mail_admins': {
|
||||
'level': 'ERROR',
|
||||
'filters': ['require_debug_false'],
|
||||
'class': 'django.utils.log.AdminEmailHandler'
|
||||
}
|
||||
},
|
||||
'loggers': {
|
||||
'django.request': {
|
||||
'handlers': ['mail_admins'],
|
||||
'level': 'ERROR',
|
||||
'propagate': True,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ADMIN2_THEME_DIRECTORY = "djadmin2theme_default"
|
||||
|
||||
########## TOOLBAR CONFIGURATION
|
||||
# See: https://github.com/django-debug-toolbar/django-debug-toolbar#installation
|
||||
INSTALLED_APPS += (
|
||||
'debug_toolbar',
|
||||
)
|
||||
|
||||
# See: https://github.com/django-debug-toolbar/django-debug-toolbar#installation
|
||||
INTERNAL_IPS = ('127.0.0.1',)
|
||||
|
||||
# See: https://github.com/django-debug-toolbar/django-debug-toolbar#installation
|
||||
MIDDLEWARE_CLASSES += (
|
||||
'debug_toolbar.middleware.DebugToolbarMiddleware',
|
||||
)
|
||||
|
||||
DEBUG_TOOLBAR_CONFIG = {
|
||||
'INTERCEPT_REDIRECTS': False,
|
||||
'SHOW_TEMPLATE_CONTEXT': True,
|
||||
}
|
||||
########## END TOOLBAR CONFIGURATION
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
from django.conf.urls import patterns, include, url
|
||||
from django.contrib import admin
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
admin.autodiscover()
|
||||
|
||||
import djadmin2
|
||||
|
||||
djadmin2.default.autodiscover()
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^admin2/', include(djadmin2.default.urls)),
|
||||
url(r'^admin/', include(admin.site.urls)),
|
||||
url(r'^$', TemplateView.as_view(template_name="home.html")),
|
||||
)
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
"""
|
||||
WSGI config for example2 project.
|
||||
|
||||
This module contains the WSGI application used by Django's development server
|
||||
and any production WSGI deployments. It should expose a module-level variable
|
||||
named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
|
||||
this application via the ``WSGI_APPLICATION`` setting.
|
||||
|
||||
Usually you will have the standard Django WSGI application here, but it also
|
||||
might make sense to replace the whole Django WSGI application with a custom one
|
||||
that later delegates to the Django one. For example, you could introduce WSGI
|
||||
middleware here, or combine a Django application with an application of another
|
||||
framework.
|
||||
|
||||
"""
|
||||
import os
|
||||
|
||||
# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks
|
||||
# if running multiple sites in the same mod_wsgi process. To fix this, use
|
||||
# mod_wsgi daemon mode with each site in its own daemon process, or use
|
||||
# os.environ["DJANGO_SETTINGS_MODULE"] = "example2.settings"
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example2.settings")
|
||||
|
||||
# This application object is used by any WSGI server configured to use this
|
||||
# file. This includes Django's development server, if the WSGI_APPLICATION
|
||||
# setting points here.
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
application = get_wsgi_application()
|
||||
|
||||
# Apply WSGI middleware here.
|
||||
# from helloworld.wsgi import HelloWorldApplication
|
||||
# application = HelloWorldApplication(application)
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example2.settings")
|
||||
|
||||
from django.core.management import execute_from_command_line
|
||||
|
||||
execute_from_command_line(sys.argv)
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
from test_models import *
|
||||
from test_views import *
|
||||
|
|
@ -1,10 +1,8 @@
|
|||
django>=1.5.0
|
||||
django-braces>=1.0.0
|
||||
djangorestframework>=2.3.3
|
||||
django-debug-toolbar>=0.9.4
|
||||
coverage>=3.6
|
||||
django-extra-views>=0.6.2
|
||||
django-floppyforms>=1.1
|
||||
Sphinx>=1.2b1
|
||||
django-filter>=0.6
|
||||
django-extra-views>=0.6.5
|
||||
django-braces>=1.3.0
|
||||
djangorestframework<=2.4.4
|
||||
django-floppyforms<=1.2
|
||||
django-filter>=0.7
|
||||
django-crispy-forms>=1.3.2
|
||||
django-debug-toolbar>=0.9.4
|
||||
pytz==2014.7
|
||||
|
|
|
|||
3
requirements_test.txt
Normal file
3
requirements_test.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
-rrequirements.txt
|
||||
pytest
|
||||
pytest-django
|
||||
45
runtests.py
45
runtests.py
|
|
@ -1,45 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'example.settings'
|
||||
exampleproject_dir = os.path.join(os.path.dirname(__file__), 'example')
|
||||
sys.path.insert(0, exampleproject_dir)
|
||||
|
||||
from django.test.utils import get_runner
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
def runtests(tests=('blog', 'files', 'djadmin2')):
|
||||
'''
|
||||
Takes a list as first argument, enumerating the apps and specific testcases
|
||||
that should be executed. The syntax is the same as for what you would pass
|
||||
to the ``django-admin.py test`` command.
|
||||
|
||||
Examples::
|
||||
|
||||
# run the default test suite
|
||||
runtests()
|
||||
|
||||
# only run the tests from application ``blog``
|
||||
runtests(['blog'])
|
||||
|
||||
# only run testcase class ``Admin2Test`` from app ``djadmin2``
|
||||
runtests(['djadmin2.Admin2Test'])
|
||||
|
||||
# run all tests from application ``blog`` and the test named
|
||||
# ``test_register`` on the ``djadmin2.Admin2Test`` testcase.
|
||||
runtests(['djadmin2.Admin2Test.test_register', 'blog'])
|
||||
'''
|
||||
TestRunner = get_runner(settings)
|
||||
test_runner = TestRunner(verbosity=1, interactive=True)
|
||||
failures = test_runner.run_tests(tests)
|
||||
sys.exit(bool(failures))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) > 1:
|
||||
tests = sys.argv[1:]
|
||||
runtests(tests)
|
||||
else:
|
||||
runtests()
|
||||
41
setup.py
41
setup.py
|
|
@ -2,6 +2,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from setuptools import setup
|
||||
from setuptools.command.test import test as TestCommand
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
|
|
@ -80,6 +81,25 @@ if sys.argv[-1] == 'publish':
|
|||
LONG_DESCRIPTION = remove_screenshots(open('README.rst').read())
|
||||
HISTORY = open('HISTORY.rst').read()
|
||||
|
||||
class PyTest(TestCommand):
|
||||
user_options = [('pytest-args=', 'a', "Arguments to pass to py.test")]
|
||||
|
||||
def initialize_options(self):
|
||||
TestCommand.initialize_options(self)
|
||||
self.pytest_args = []
|
||||
|
||||
def finalize_options(self):
|
||||
TestCommand.finalize_options(self)
|
||||
self.test_args = []
|
||||
self.test_suite = True
|
||||
|
||||
def run_tests(self):
|
||||
#import here, cause outside the eggs aren't loaded
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'example'))
|
||||
import pytest
|
||||
errno = pytest.main(self.pytest_args)
|
||||
sys.exit(errno)
|
||||
|
||||
setup(
|
||||
name='django-admin2',
|
||||
version=version,
|
||||
|
|
@ -105,16 +125,21 @@ setup(
|
|||
license='MIT',
|
||||
packages=get_packages('djadmin2'),
|
||||
include_package_data=True,
|
||||
test_suite='runtests.runtests',
|
||||
#test_suite='runtests.runtests',
|
||||
install_requires=[
|
||||
'django>=1.5.0',
|
||||
'django-braces>=1.0.0',
|
||||
'django-extra-views>=0.6.2',
|
||||
'djangorestframework>=2.3.3',
|
||||
'django-floppyforms>=1.1',
|
||||
'django-filter>=0.6',
|
||||
'django-crispy-forms>=1.3.2'
|
||||
'django>=1.6.0',
|
||||
'django-extra-views>=0.6.5',
|
||||
'django-braces>=1.3.0',
|
||||
'djangorestframework<=2.4.4',
|
||||
'django-floppyforms<=1.2',
|
||||
'django-filter>=0.7',
|
||||
'django-crispy-forms>=1.3.2',
|
||||
'pytz==2014.7'
|
||||
],
|
||||
extras_require={
|
||||
'testing': ['pytest', 'pytest-django', 'pytest-ipdb'],
|
||||
},
|
||||
cmdclass = {'test': PyTest},
|
||||
zip_safe=False,
|
||||
)
|
||||
|
||||
|
|
|
|||
32
tox.ini
32
tox.ini
|
|
@ -1,35 +1,17 @@
|
|||
[tox]
|
||||
# for py 3.x we are using only django 1.6.x as 1.5.x had only "experimental py3 support"
|
||||
envlist = py27-dj1.4.x, py27-dj1.5.x, py27-dj1.6.x, py27-dj1.7.x,
|
||||
py33-dj1.6.x, py34-dj1.6.x, py33-dj1.7.x, py34-dj1.7.x,
|
||||
pypy-dj1.6.x, pypy3-dj1.6.x,
|
||||
envlist = py27-dj1.6.x, py27-dj1.7.x, py33-dj1.6.x, py34-dj1.6.x,
|
||||
py33-dj1.7.x, py34-dj1.7.x, pypy-dj1.6.x, pypy3-dj1.6.x,
|
||||
skipsdist = True
|
||||
|
||||
[testenv]
|
||||
commands = python runtests.py
|
||||
deps =
|
||||
django-braces>=1.0.0
|
||||
django-extra-views>=0.6.2
|
||||
djangorestframework>=2.3.3
|
||||
django-floppyforms>=1.1
|
||||
django-filter>=0.6
|
||||
django-crispy-forms>=1.3.2
|
||||
django-debug-toolbar>=0.9.4
|
||||
|
||||
[testenv:py27-dj1.4.x]
|
||||
basepython=python2.7
|
||||
deps =
|
||||
Django>=1.4,<1.5
|
||||
{[testenv]deps}
|
||||
|
||||
[testenv:py27-dj1.5.x]
|
||||
basepython=python2.7
|
||||
deps =
|
||||
Django>=1.5,<1.6
|
||||
{[testenv]deps}
|
||||
commands = py.test []
|
||||
deps = -rrequirements_test.txt
|
||||
setenv=
|
||||
DJANGO_SETTINGS_MODULE = example.settings
|
||||
PYTHONPATH = {toxinidir}/example:{toxinidir}
|
||||
|
||||
[testenv:py27-dj1.6.x]
|
||||
commands = coverage run runtests.py
|
||||
basepython=python2.7
|
||||
deps =
|
||||
Django>=1.6,<1.7
|
||||
|
|
|
|||
Loading…
Reference in a new issue