I have a view--called DevicesListView below--that executes without a problem in the test environment (in Webtest), but when I try to execute the same view in the development environment, I get a NoReverseMatch error.
devices/urls.py
from inventory.devices.views import DeviceCheckout
urlpatterns = patterns('',
# NOTE: these are namespaced as 'devices'
# ex: /devices
url(r'^$',
DevicesListView.as_view(),
name='index'),
# ex: /devices/3/checkout
url(r'^(?P<pk>\d)/checkout/$',
DeviceCheckout.as_view(),
name='checkout'),
)
views.py
class DevicesListView(ListView):
'''Index view for devices.'''
model = Device
template_name = 'devices/index.html'
context_object_name = 'all_devices'
def get(self, request):
# ...
def post(self, request):
"""Process the action for the selected devices.
"""
action = request.POST['action'] # the selected action
# Get the IDs of the devices that had their checkboxes selected
selected_pks = [int(v) for v in request.POST.getlist('device_select')]
# Get the selected Device objects
selected_devices = Device.objects.filter(pk__in=selected_pks)
# ... Logic to handle selected action ...
# redirect to lendee selection page
device = selected_devices[0]
if action == 'checkout_selected':
return redirect('devices:checkout', pk=device.pk) # throws NoReverseMatch in development
elif action == 'checkin_selected':
return redirect('devices:checkin', pk=device.pk)
return redirect('devices:index')
webtest_tests.py
from django_webtest import WebTest
from inventory.user.tests.factories import UserFactory
from inventory.devices.tests.factories import DeviceFactory
class TestAUser(WebTest):
def setUp(self):
self.user = UserFactory()
def test_can_checkout_device(self):
# two devices are already created
DeviceFactory()
DeviceFactory()
# goes to devices page
res = self.app.get('/devices', user=self.user)
# checks the first device
form = res.forms['device_control']
form.set('device_select', True, index=0)
# Selects Checkout device
form.set('action', 'checkout_selected')
# Submits form
res = form.submit().follow()
assert_equal(res.status_code, 200) # PASSES
devices/index.html
<form method="post" id="device_control" action="">
{% csrf_token %}
<label><span class="action-label">Action:</span>
<select name="action">
<option value="" selected="selected">---------</option>
<option value="checkout_selected">Checkout</option>
<option value="checkin_selected">Checkin</option>
<option value="delete_selected">Delete selected</option>
</select>
</label>
<input type="submit" name="_save" class="default btn" value="Go"/>
</div><!-- end btn-toolbar -->
<table class='table table-striped'>
<thead>
<th>Select</th>
<th>Name</th>
</thead>
<tbody>
{% for device in all_devices %}
<tr>
<td><input type="checkbox" value="{{ device.pk }}" name='device_select' id="select_device{{ device.pk }}"></td>
<td>{{ device.name }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</form>
The template error I get is:
NoReverseMatch at /devices/
Reverse for 'checkout' with arguments '()' and keyword arguments '{'pk': 13}' not found.
I have made sure to syncdb and restart the development server. What would cause this different behavior between the test and development environments? I am using Django 1.5 on Mac OSX Lion.
A silly mistake indeed. The problem was in the url pattern regex. r'^(?P<pk>\d)/checkout/$'
was only capturing 1-digit pk's. It was missing a +
after \d
. So it should be:
...
# ex: /devices/13/checkout
url(r'^(?P<pk>\d+)/checkout/$',
DeviceCheckout.as_view(),
name='checkout'),
...