Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update individual item for instance case #39

Merged
merged 4 commits into from Nov 19, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
42 changes: 0 additions & 42 deletions django_object_actions/tests/test_utils.py
@@ -1,12 +1,9 @@
from django.db.models.query import QuerySet
from django.test import TestCase
from django.utils.unittest import expectedFailure

from example_project.polls.models import Poll

from ..utils import (
BaseDjangoObjectActions,
QuerySetIsh,
takes_instance_or_queryset,
)

Expand Down Expand Up @@ -66,42 +63,6 @@ def test_get_djoa_button_attrs_custom_attrs_get_partitioned(self):
self.assertEqual(custom['nonstandard'], 'wombat')


class QuerySetIshTest(TestCase):
fixtures = ['sample_data']

def setUp(self):
# WISHLIST don't depend on fixture
self.obj = Poll.objects.get(pk=1)

def test_can_turn_object_into_queryset(self):
qs = QuerySetIsh(self.obj)
self.assertEqual(qs.count(), 1)
self.assertEqual(qs.get(), self.obj)
self.assertEqual(qs.order_by('foo').get(), self.obj)
self.assertEqual(qs.all().get(), self.obj)
self.assertEqual(qs.filter().get(), self.obj)
self.assertEqual(qs.latest('bar'), self.obj)

def test_queryset_supports_delete(self):
qs = QuerySetIsh(self.obj)
qs.delete()
with self.assertRaises(Poll.DoesNotExist):
Poll.objects.get(pk=1)

@expectedFailure
def test_queryset_supports_filter(self):
# yeah, we don't actually support doing this, but it would be nice.
qs = QuerySetIsh(self.obj)
with self.assertRaises(Poll.DoesNotExist):
# this should be empty because the question is just `"hi"`
qs.filter(question='abra cadabra').get()

def test_queryset_supports_update(self):
qs = QuerySetIsh(self.obj)
qs.update(question='mooo')
self.assertEqual(Poll.objects.get(pk=1).question, 'mooo')


class DecoratorTest(TestCase):
fixtures = ['sample_data']

Expand All @@ -128,17 +89,14 @@ def myfunc(foo, bar, queryset):

# passing in an instance yields a queryset (using positional args)
queryset = myfunc(None, None, self.obj)
self.assertIsInstance(queryset, QuerySet)
# the resulting queryset only has one item and it's self.obj
self.assertEqual(queryset.get(), self.obj)

# passing in a queryset yields the same queryset
queryset = myfunc(None, None, self.queryset)
self.assertIsInstance(queryset, QuerySet)
self.assertEqual(queryset, self.queryset)

# passing in an instance yields a queryset (using keyword args)
queryset = myfunc(None, None, queryset=self.obj)
self.assertIsInstance(queryset, QuerySet)
# the resulting queryset only has one item and it's self.obj
self.assertEqual(queryset.get(), self.obj)
42 changes: 11 additions & 31 deletions django_object_actions/utils.py
Expand Up @@ -136,43 +136,23 @@ def message_user(self, request, message):
messages.info(request, message)


class QuerySetIsh(QuerySet):
"""
Takes an instance and mimics it coming from a QuerySet.

This is a hack to support the `takes_instance_or_queryset` decorator so
that you can re-use functions written for standard Django admin actions and
use them for Object Tools too.
"""
def __init__(self, instance=None, *args, **kwargs):
try:
model = instance._meta.model
except AttributeError:
# Django 1.5 does this instead, getting the model may be overkill
# we may be able to throw away all this logic
model = instance._meta.concrete_model
self._doa_instance = instance
super(QuerySetIsh, self).__init__(model, *args, **kwargs)
self._result_cache = [instance]

def _clone(self, *args, **kwargs):
# don't clone me, bro
return self

def get(self, *args, **kwargs):
# Starting in Django 1.7, `QuerySet.get` started slicing to
# `MAX_GET_RESULTS`, so to avoid messing with `__getslice__`, override
# `.get`.
return self._doa_instance


def takes_instance_or_queryset(func):
"""Decorator that makes standard Django admin actions compatible."""
@wraps(func)
def decorated_function(self, request, queryset):
# func follows the prototype documented at:
# https://docs.djangoproject.com/en/dev/ref/contrib/admin/actions/#writing-action-functions
if not isinstance(queryset, QuerySet):
queryset = QuerySetIsh(queryset)
try:
# Django >=1.8
queryset = self.get_queryset(request).filter(pk=queryset.pk)
except AttributeError:
try:
# Django >=1.6,<1.8
model = queryset._meta.model
except AttributeError:
# Django <1.6
model = queryset._meta.concrete_model
queryset = model.objects.filter(pk=queryset.pk)
return func(self, request, queryset)
return decorated_function
8 changes: 8 additions & 0 deletions example_project/polls/fixtures/sample_data.json
Expand Up @@ -7,6 +7,14 @@
"question": "Do you like me?"
}
},
{
"pk": 2,
"model": "polls.poll",
"fields": {
"pub_date": "2012-10-20T18:20:38",
"question": "Do you wanna build a snow man?"
}
},
{
"pk": 1,
"model": "polls.choice",
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
@@ -1,6 +1,6 @@
Django==1.8.4
dj-database-url==0.3.0
django-extensions==1.5.5
django-extensions==1.5.9
tox==2.1.1
factory-boy==2.5.2
coverage==3.7.1
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Expand Up @@ -17,7 +17,7 @@ commands =
{envpython} example_project/manage.py test django_object_actions
deps =
dj_database_url==0.3.0
django-extensions==1.4.6
django-extensions==1.5.9
factory-boy==2.4.1
coveralls: coverage
django14: Django<1.5
Expand Down