Merge pull request #403 from galuszkak/tox_tests

Many fixes to py3 support and django 1.7 compatybility
This commit is contained in:
Andrews Medina 2015-01-08 15:54:53 -02:00
commit 59990bbef5
69 changed files with 375 additions and 569 deletions

View file

@ -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

View file

@ -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

View file

@ -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
-----------

View file

@ -48,7 +48,7 @@ Screenshots
Requirements
=============
* Django 1.5+
* Django 1.6+
* Python 2.7+ or Python 3.3+
* django-braces_
* django-extra-views_

View file

@ -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)

View file

@ -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):

View file

@ -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

View file

@ -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)

View file

@ -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 = {

View file

@ -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):

View file

@ -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):

View file

@ -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 *

View file

@ -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):

View file

@ -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'

View file

@ -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()

View file

@ -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):

View file

@ -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,

View file

@ -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)

View file

@ -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):

View file

@ -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):

View file

@ -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 *

View file

@ -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

View file

@ -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"))

View file

@ -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

View file

@ -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")

View file

@ -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"

View file

@ -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')

View file

@ -1,2 +0,0 @@
from test_models import *
from test_views import *

View file

@ -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],

View file

@ -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:

View file

@ -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],

View file

@ -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

View file

@ -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")),
)

View file

@ -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)

View file

@ -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)

View file

@ -1,2 +0,0 @@
from test_models import *
from test_views import *

View file

@ -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
View file

@ -0,0 +1,3 @@
-rrequirements.txt
pytest
pytest-django

View file

@ -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()

View file

@ -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
View file

@ -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