From 720e58a27d37e591da37c37e74c5ba6983ca012c Mon Sep 17 00:00:00 2001 From: Robert Wells Date: Thu, 5 Jul 2018 11:31:21 -0700 Subject: [PATCH] Added tests for args/kwargs and also added docs fr it --- .gitignore | 1 + docs/usage.md | 168 +++++++++++++++++++---------- easy_scoping/tests/test_scoping.py | 50 +++++++++ easy_scoping/widgets/models.py | 5 + 4 files changed, 168 insertions(+), 56 deletions(-) diff --git a/.gitignore b/.gitignore index 1ee75e1..ad32dbf 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ dist/ bin include lib +db.sqlite3 .DS_Store diff --git a/docs/usage.md b/docs/usage.md index 7ca94ff..9292e43 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -3,65 +3,13 @@ id: usage title: Usage --- +Example Django Model located at bottom of page. -## Example Django Model - -```python - - -from django.db import models -from .options import COLORS, SIZES, SHAPES -from ScopingMixin import ScopingMixin, ScopingQuerySet -import datetime - - -class Widget(ScopingMixin, models.Model): - name = models.CharField(max_length=30) - color = models.CharField(choices=COLORS, max_length=30) - size = models.CharField(choices=SIZES, max_length=30) - shape = models.CharField(choices=SHAPES, max_length=30) - used_on = models.DateField(default=datetime.date.today) - - objects = ScopingQuerySet.as_manager() - - - def get_name(self): - return self.name - - def get_color(self): - return self.color - def get_size(self): - return self.size - def get_shape(self): - return self.shape +## Basic Usage - def get_used_on(self): - return self.used_on - -# Scopes for filtering -Widget.scope('basic_filter_widget', lambda qs: qs.f(color='blue', - size='small', - shape='circle')) -Widget.scope('blue', lambda qs: qs.f(color='blue')) -Widget.scope('small', lambda qs: qs.f(size='small')) -Widget.scope('circle', lambda qs: qs.f(shape='circle')) -Widget.scope('before_y2k', lambda qs: qs.f(used_on__lte=datetime.date(2000,1,1))) - -# Scopes for excluding -Widget.scope('basic_exclude_widget', lambda qs: qs.e(color='blue', - size='small', - shape='circle')) -Widget.scope('not_blue', lambda qs: qs.e(color='blue')) -Widget.scope('not_small', lambda qs: qs.e(size='small')) -Widget.scope('not_circle', lambda qs: qs.e(shape='circle')) -Widget.scope('not_before_y2k', lambda qs: qs.e(used_on__lte=datetime.date(2000,1,1))) -``` - -## Usage - -### Basic Scoping +### Scoping Now, Let's say we wanted to find all of the widgets in our database which were blue. @@ -76,7 +24,7 @@ With easy scoping: Widget.a().blue() ``` -### Basic Scoping Multiple Fields +### Scoping Multiple Fields How about finding all of the widgets which are blue, small, and a circle. Without easy scoping: @@ -144,3 +92,111 @@ alphabetical order. ```python Widget.a().blue().small().not_circle().order_by('color') ``` + +## Advanced Usage + +Using lambda functions, and that scopes take *args, **kwargs, you can create +some really interesting scopes to simply your workflow. + +### Arguments + +You could have a scope that filters on colors but instead of having one for +`blue` and one for `green` we can just have do something like this. + +```python +Widget.scope('colors', lambda queryset, value: queryset.f(color=value)) +# Then this works for any color +Widget.a().color('blue') +``` + +### Multiple Arguments + +You can also make scopes on more than one argument, let's check out one for +color and size + +```python +Widget.scope('color_size', lambda queryset, c_value, s_value: queryset.f(color=value, size=s_value)) +# Then you can just pass like this +Widget.a().color_size('blue', 'small') +``` + +### Keyword Arguments + +The problem with args is that you have to remember the order you set up when you +defined the scope. You can just use kwargs instead! + +```python +Widget.scope('foo', lambda queryset, **kwargs: queryset.f(**kwargs)) +# Then this works for any color +Widget.a().foo(color='blue') +``` + +### Multiple Keyword Arguments + +With this you can just go nuts on filtering, but this is basically just using +the actual `.filter()` method. + +```python +Widget.scope('foo', lambda queryset, **kwargs: queryset.f(**kwargs)) +# Then this works for any color +Widget.a().foo(color='blue', size='small') +``` + + + + + +## Example Django Model + +```python + + +from django.db import models +from .options import COLORS, SIZES, SHAPES +from ScopingMixin import ScopingMixin, ScopingQuerySet +import datetime + + +class Widget(ScopingMixin, models.Model): + name = models.CharField(max_length=30) + color = models.CharField(choices=COLORS, max_length=30) + size = models.CharField(choices=SIZES, max_length=30) + shape = models.CharField(choices=SHAPES, max_length=30) + used_on = models.DateField(default=datetime.date.today) + + objects = ScopingQuerySet.as_manager() + + + def get_name(self): + return self.name + + def get_color(self): + return self.color + + def get_size(self): + return self.size + + def get_shape(self): + return self.shape + + def get_used_on(self): + return self.used_on + +# Scopes for filtering +Widget.scope('basic_filter_widget', lambda qs: qs.f(color='blue', + size='small', + shape='circle')) +Widget.scope('blue', lambda qs: qs.f(color='blue')) +Widget.scope('small', lambda qs: qs.f(size='small')) +Widget.scope('circle', lambda qs: qs.f(shape='circle')) +Widget.scope('before_y2k', lambda qs: qs.f(used_on__lte=datetime.date(2000,1,1))) + +# Scopes for excluding +Widget.scope('basic_exclude_widget', lambda qs: qs.e(color='blue', + size='small', + shape='circle')) +Widget.scope('not_blue', lambda qs: qs.e(color='blue')) +Widget.scope('not_small', lambda qs: qs.e(size='small')) +Widget.scope('not_circle', lambda qs: qs.e(shape='circle')) +Widget.scope('not_before_y2k', lambda qs: qs.e(used_on__lte=datetime.date(2000,1,1))) +``` diff --git a/easy_scoping/tests/test_scoping.py b/easy_scoping/tests/test_scoping.py index 12b9419..3c67fde 100644 --- a/easy_scoping/tests/test_scoping.py +++ b/easy_scoping/tests/test_scoping.py @@ -16,6 +16,56 @@ def test_db_data_loaded(self): obj = obj.a() self.assertEqual(obj.count(), 336) + def test_passing_many_kwargs(self): + obj1 = Widget.objects.filter(color='blue', + size='small') + obj2 = Widget.a().take_kwargs(color='blue', size='small') + + self.assertQuerysetEqual(obj1, + obj2, + transform=lambda x: x, + ordered=False) + self.assertEqual(obj1.count(), obj2.count()) + + obj3 = Widget.a().take_kwargs(size='small', color='blue') + + self.assertQuerysetEqual(obj1, + obj3, + transform=lambda x: x, + ordered=False) + self.assertEqual(obj1.count(), obj3.count()) + + def test_passing_kwargs(self): + obj1 = Widget.objects.filter(color='blue') + obj2 = Widget.a().take_kwargs(color='blue') + + self.assertQuerysetEqual(obj1, + obj2, + transform=lambda x: x, + ordered=False) + self.assertEqual(obj1.count(), obj2.count()) + + def test_passing_many_args(self): + obj1 = Widget.objects.filter(color='blue', + size='small') + obj2 = Widget.a().take_more_args('blue', 'small') + + self.assertQuerysetEqual(obj1, + obj2, + transform=lambda x: x, + ordered=False) + self.assertEqual(obj1.count(), obj2.count()) + + def test_passing_args(self): + obj1 = Widget.objects.filter(color='blue') + obj2 = Widget.a().take_args('blue') + + self.assertQuerysetEqual(obj1, + obj2, + transform=lambda x: x, + ordered=False) + self.assertEqual(obj1.count(), obj2.count()) + def test_no_scope_registered(self): with self.assertRaises(AttributeError): Widget.a().not_a_scope() diff --git a/easy_scoping/widgets/models.py b/easy_scoping/widgets/models.py index c375c52..f76e9fb 100644 --- a/easy_scoping/widgets/models.py +++ b/easy_scoping/widgets/models.py @@ -48,3 +48,8 @@ def get_used_on(self): Widget.scope('not_circle', lambda qs: qs.e(shape='circle')) Widget.scope('not_before_y2k', lambda qs: qs.e(used_on__lte=datetime.date(2000,1,1))) Widget.scope('not_after_y2k', lambda qs: qs.e(used_on__gte=datetime.date(2000,1,1))) + +# Scope takes argument +Widget.scope('take_args', lambda qs, i: qs.f(color=i)) +Widget.scope('take_more_args', lambda qs, i, r: qs.f(color=i, size=r)) +Widget.scope('take_kwargs', lambda qs, **kwargs: qs.f(**kwargs))