Skip to content

Commit

Permalink
Add proper return value to MockSet delete (#184)
Browse files Browse the repository at this point in the history
  • Loading branch information
nfantone authored Sep 9, 2024
1 parent e7d8c41 commit 8736200
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 7 deletions.
30 changes: 25 additions & 5 deletions django_mock_queries/query.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import datetime
import random
from collections import OrderedDict, namedtuple
from collections import OrderedDict, defaultdict, namedtuple
from unittest.mock import Mock, MagicMock, PropertyMock

from .constants import *
from .exceptions import *
from .utils import (
matches, get_attribute, validate_mock_set, is_list_like_iter, flatten_list, truncate,
hash_dict, filter_results
hash_dict, filter_results, get_nested_attr
)


Expand Down Expand Up @@ -267,16 +267,25 @@ def update(self, **attrs):
return count

def _delete_recursive(self, *items_to_remove, **attrs):
removed_items = defaultdict(int)

for item in matches(*items_to_remove, **attrs):
self.items.remove(item)
self.fire(item, self.EVENT_DELETED)

# Support returning detailed information about removed items
# even if items are not `MockModel` instances
item_label = get_nested_attr(item, '_meta.label', default=type(item).__name__)
removed_items[item_label] += 1

if self.clone is not None:
self.clone._delete_recursive(*items_to_remove, **attrs)

return sum(removed_items.values()), removed_items

def delete(self, **attrs):
# Delete normally doesn't take **attrs - they're only needed for remove
self._delete_recursive(*self.items, **attrs)
return self._delete_recursive(*self.items, **attrs)

# The following 2 methods were kept for backwards compatibility and
# should be removed in the future since they are covered by filter & delete
Expand Down Expand Up @@ -437,7 +446,8 @@ def __init__(self, *args, **kwargs):
super(MockModel, self).__init__(*args, **kwargs)

self.save = PropertyMock()
self.__meta = MockOptions(*self.get_fields())
object_name = self.get('mock_name', type(self).__name__)
self.__meta = MockOptions(object_name, *self.get_fields())

def __getattr__(self, item):
return self.get(item, None)
Expand Down Expand Up @@ -471,9 +481,19 @@ def create_model(*fields):


class MockOptions:
def __init__(self, *field_names):
def __init__(self, object_name, *field_names):
self.load_fields(*field_names)
self.get_latest_by = None
self.object_name = object_name
self.model_name = object_name.lower()

@property
def label(self):
return self.object_name

@property
def label_lower(self):
return self.model_name

def load_fields(self, *field_names):
fields = {name: MockField(name) for name in field_names}
Expand Down
10 changes: 10 additions & 0 deletions django_mock_queries/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,3 +332,13 @@ def _filter_single_q(source, q_obj, negated):
return filter_results(source, q_obj)
else:
return matches(negated=negated, *source, **{q_obj[0]: q_obj[1]})


def get_nested_attr(obj, attr_path, default=None):
attrs = attr_path.split('.')
try:
for attr in attrs:
obj = getattr(obj, attr)
return obj
except AttributeError:
return default
16 changes: 14 additions & 2 deletions tests/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -706,20 +706,32 @@ def test_query_delete_all_entries(self):
item_2 = MockModel(foo=1, bar='b', mock_name='item_2')

self.mock_set.add(item_1, item_2)
self.mock_set.delete()
deleted_count, deleted_items = self.mock_set.delete()

assert len(self.mock_set) == 0, len(self.mock_set)
assert deleted_count == 2
assert deleted_items == {'item_1': 1, 'item_2': 1}

def test_query_delete_non_model_entries(self):
items = [1, 2, 3, 'foo', 'bar', True]
self.mock_set.add(*items)

deleted_count, deleted_items = self.mock_set.delete()
assert deleted_count == 6
assert deleted_items == {'int': 3, 'str': 2, 'bool': 1}

def test_query_delete_entries_propagated_from_nested_qs(self):
item_1 = MockModel(foo=1, bar='a', mock_name='item_1')
item_2 = MockModel(foo=1, bar='b', mock_name='item_2')

self.mock_set.add(item_1, item_2)
self.mock_set.filter(bar='b').delete()
deleted_count, deleted_items = self.mock_set.filter(bar='b').delete()

assert len(self.mock_set) == 1, len(self.mock_set)
assert item_1 in self.mock_set
assert item_2 not in self.mock_set
assert deleted_count == 1
assert deleted_items == {'item_2': 1}

def test_query_gets_unique_match_by_attrs_from_set(self):
item_1 = MockModel(foo=1)
Expand Down

0 comments on commit 8736200

Please sign in to comment.