Skip to content

Commit 04eb0d8

Browse files
committed
Merge pull request #43 from Amareis/issue-42
Fix Django 1.9 compatibility, Drop Django 1.4
2 parents fae7d93 + fd28613 commit 04eb0d8

File tree

5 files changed

+40
-20
lines changed

5 files changed

+40
-20
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ language: python
22
# Generate this env list with:
33
# tox -l | awk '{ print " - TOX_ENV="$0}'
44
env:
5-
- TOX_ENV=django14-py26
6-
- TOX_ENV=django14-py27
75
- TOX_ENV=django15-py26
86
- TOX_ENV=django15-py27
97
- TOX_ENV=django16-py26
@@ -14,6 +12,8 @@ env:
1412
- TOX_ENV=django17-py34
1513
- TOX_ENV=django18-py27
1614
- TOX_ENV=django18-py34
15+
- TOX_ENV=django19-py27
16+
- TOX_ENV=django19-py34
1717
# pending full travisci support for python 3.5
1818
# - TOX_ENV=django18-py35
1919
- TOX_ENV=coveralls-django18-py27

django_object_actions/templates/django_object_actions/change_form.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
{% block object-tools-items %}
55
{% for tool in objectactions %}
66
<li class="objectaction-item" data-tool-name="{{ tool.name }}">
7-
<a href='tools/{{ tool.name }}/' title="{{ tool.standard_attrs.title }}"
7+
<a href='{% url tools_view_name pk=object_id tool=tool.name %}' title="{{ tool.standard_attrs.title }}"
88
{% for k, v in tool.custom_attrs.items %}
99
{{ k }}="{{ v }}"
1010
{% endfor %}

django_object_actions/tests/tests.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from django.core.urlresolvers import reverse
12
from django.test import TestCase
23

34
from example_project.polls.factories import UserFactory
@@ -19,56 +20,61 @@ class AppTests(LoggedInTestCase):
1920

2021
def test_bare_mixin_works(self):
2122
# hit admin that doesn't have any tools defined, just the mixin
22-
response = self.client.get('/admin/polls/poll/add/')
23+
response = self.client.get(reverse('admin:polls_poll_add'))
2324
self.assertEqual(response.status_code, 200)
2425

2526
def test_configured_mixin_works(self):
2627
# hit admin that does have any tools defined
27-
response = self.client.get('/admin/polls/choice/add/')
28+
response = self.client.get(reverse('admin:polls_choice_add'))
2829
self.assertEqual(response.status_code, 200)
2930
self.assertIn('objectactions', response.context_data)
3031

3132
def test_tool_func_gets_executed(self):
3233
c = Choice.objects.get(pk=1)
3334
votes = c.votes
34-
response = self.client.get('/admin/polls/choice/1/tools/increment_vote/')
35+
response = self.client.get(reverse('admin:polls_choice_tools', args=(1, 'increment_vote')))
3536
self.assertEqual(response.status_code, 302)
36-
self.assertTrue(response['location'].endswith('/admin/polls/choice/1/'))
37+
url = reverse('admin:polls_choice_change', args=(1,))
38+
self.assertTrue(response['location'].endswith(url))
3739
c = Choice.objects.get(pk=1)
3840
self.assertEqual(c.votes, votes + 1)
3941

4042
def test_tool_can_return_httpresponse(self):
4143
# we know this url works because of fixtures
42-
url = '/admin/polls/choice/2/tools/edit_poll/'
44+
url = reverse('admin:polls_choice_tools', args=(2, 'edit_poll'))
4345
response = self.client.get(url)
4446
# we expect a redirect
4547
self.assertEqual(response.status_code, 302)
46-
self.assertTrue(response['location'].endswith('/admin/polls/poll/1/'))
48+
self.assertTrue(response['location'].endswith(reverse('admin:polls_poll_change', args=(1,))))
4749

4850
def test_can_return_template(self):
4951
# This is more of a test of render_to_response than the app, but I think
5052
# it's good to document that this is something we can do.
51-
url = '/admin/polls/poll/1/tools/delete_all_choices/'
53+
url = reverse('admin:polls_poll_tools', args=(1, 'delete_all_choices'))
5254
response = self.client.get(url)
5355
self.assertTemplateUsed(response, "clear_choices.html")
5456

5557
def test_message_user_sends_message(self):
56-
url = '/admin/polls/poll/1/tools/delete_all_choices/'
58+
url = reverse('admin:polls_poll_tools', args=(1, 'delete_all_choices'))
5759
self.assertNotIn('messages', self.client.cookies)
5860
self.client.get(url)
5961
self.assertIn('messages', self.client.cookies)
6062

6163
def test_intermediate_page_with_post_works(self):
6264
self.assertTrue(Choice.objects.filter(poll=1).count())
63-
url = '/admin/polls/poll/1/tools/delete_all_choices/'
65+
url = reverse('admin:polls_poll_tools', args=(1, 'delete_all_choices'))
6466
response = self.client.post(url)
6567
self.assertEqual(response.status_code, 302)
6668
self.assertEqual(Choice.objects.filter(poll=1).count(), 0)
6769

6870
def test_undefined_tool_404s(self):
69-
response = self.client.get('/admin/polls/choice/1/tools/weeeewoooooo/')
71+
response = self.client.get(reverse('admin:polls_poll_tools', args=(1, 'weeeewoooooo')))
7072
self.assertEqual(response.status_code, 404)
7173

7274
def test_key_error_tool_500s(self):
7375
self.assertRaises(KeyError, self.client.get,
74-
'/admin/polls/choice/1/tools/raise_key_error/')
76+
reverse('admin:polls_choice_tools', args=(1, 'raise_key_error')))
77+
78+
def test_render_button(self):
79+
response = self.client.get(reverse('admin:polls_choice_change', args=(1,)))
80+
self.assertEqual(response.status_code, 200)

django_object_actions/utils.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from django.conf.urls import patterns, url
66
from django.contrib import messages
7+
from django.core.urlresolvers import reverse
78
from django.db.models.query import QuerySet
89
from django.http import Http404, HttpResponse, HttpResponseRedirect
910
from django.views.generic import View
@@ -14,17 +15,28 @@ class BaseDjangoObjectActions(object):
1415
"""ModelAdmin mixin to add object-tools just like adding admin actions."""
1516
# list to hold each object action tool
1617
objectactions = []
18+
tools_view_name = None
1719

18-
def get_tool_urls(self):
20+
def get_tool_urls(self, urls):
1921
"""Gets the url patterns that route each tool to a special view."""
2022
tools = {}
23+
24+
end = '_change'
25+
for url_pattern in urls:
26+
if url_pattern.name.endswith(end):
27+
tools_view = url_pattern.name[:-len(end)] + '_tools'
28+
change_view = 'admin:' + url_pattern.name
29+
self.tools_view_name = 'admin:' + tools_view
30+
break
31+
2132
for tool in self.objectactions:
2233
tools[tool] = getattr(self, tool)
2334
my_urls = patterns('',
2435
# supports pks that are numbers or uuids
2536
url(r'^(?P<pk>[0-9a-f\-]+)/tools/(?P<tool>\w+)/$',
2637
self.admin_site.admin_view(
27-
ModelToolsView.as_view(model=self.model, tools=tools)))
38+
ModelToolsView.as_view(model=self.model, tools=tools, back=change_view)),
39+
name=tools_view)
2840
)
2941
return my_urls
3042

@@ -35,7 +47,7 @@ def get_tool_urls(self):
3547
def get_urls(self):
3648
"""Prepends `get_urls` with our own patterns."""
3749
urls = super(BaseDjangoObjectActions, self).get_urls()
38-
return self.get_tool_urls() + urls
50+
return self.get_tool_urls(urls) + urls
3951

4052
def render_change_form(self, request, context, **kwargs):
4153
"""Puts `objectactions` into the context."""
@@ -55,6 +67,7 @@ def to_dict(tool_name):
5567
to_dict,
5668
self.get_object_actions(request, context, **kwargs)
5769
)
70+
context['tools_view_name'] = self.tools_view_name
5871
return super(BaseDjangoObjectActions, self).render_change_form(
5972
request, context, **kwargs)
6073

@@ -106,6 +119,7 @@ class DjangoObjectActions(BaseDjangoObjectActions):
106119
class ModelToolsView(SingleObjectMixin, View):
107120
"""A special view that run the tool's callable."""
108121
tools = {}
122+
back = None
109123

110124
def get(self, request, **kwargs):
111125
# SingleOjectMixin's `get_object`. Works because the view
@@ -118,7 +132,7 @@ def get(self, request, **kwargs):
118132
ret = tool(request, obj)
119133
if isinstance(ret, HttpResponse):
120134
return ret
121-
back = request.path.rsplit('/', 3)[0] + '/'
135+
back = reverse(self.back, args=(kwargs['pk'],))
122136
return HttpResponseRedirect(back)
123137

124138
# HACK to allow POST requests too easily

tox.ini

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
# https://docs.djangoproject.com/en/dev/faq/install/#what-python-version-can-i-use-with-django
33
[tox]
44
envlist =
5-
django14-{py26,py27},
65
django15-{py26,py27},
76
django16-{py26,py27,py33},
87
django17-{py27,py33,py34},
98
django18-{py27,py34,py35},
9+
django19-{py27,py34,py35},
1010
# run one of the tests again but with coverage
1111
coveralls-django18-py27,
1212
skipsdist = True
@@ -21,11 +21,11 @@ deps =
2121
django-extensions==1.5.9
2222
factory-boy==2.6.0
2323
coveralls: coverage
24-
django14: Django<1.5
2524
django15: Django<1.6
2625
django16: Django<1.7
2726
django17: Django<1.8
2827
django18: Django<1.9
28+
django19: Django<1.10
2929

3030
[testenv:coveralls-django18-py27]
3131
commands =

0 commit comments

Comments
 (0)