Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-django-cacheops for
openSUSE:Factory checked in at 2023-11-13 22:18:00
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-django-cacheops (Old)
and /work/SRC/openSUSE:Factory/.python-django-cacheops.new.17445 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-django-cacheops"
Mon Nov 13 22:18:00 2023 rev:10 rq:1124878 version:7.0.2
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-django-cacheops/python-django-cacheops.changes
2023-05-10 16:18:14.086871882 +0200
+++
/work/SRC/openSUSE:Factory/.python-django-cacheops.new.17445/python-django-cacheops.changes
2023-11-13 22:20:37.682966331 +0100
@@ -1,0 +2,10 @@
+Fri Nov 10 12:25:25 UTC 2023 - Dirk Müller <[email protected]>
+
+- update to 7.0.2:
+ * fixed .aggregate()
+ * fixed big memory usage during migrations
+ * fixed INSIDEOUT in older redises
+ * better handle model families with abstracts in them
+ * allow funcy 2.0+
+
+-------------------------------------------------------------------
Old:
----
django-cacheops-7.0.1.tar.gz
New:
----
django-cacheops-7.0.2.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-django-cacheops.spec ++++++
--- /var/tmp/diff_new_pack.wj7dcZ/_old 2023-11-13 22:20:38.186984888 +0100
+++ /var/tmp/diff_new_pack.wj7dcZ/_new 2023-11-13 22:20:38.186984888 +0100
@@ -19,7 +19,7 @@
%define skip_python2 1
%define skip_python36 1
Name: python-django-cacheops
-Version: 7.0.1
+Version: 7.0.2
Release: 0
Summary: Django ORM cache with automatic granular event-driven
invalidation
License: BSD-3-Clause
++++++ django-cacheops-7.0.1.tar.gz -> django-cacheops-7.0.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-cacheops-7.0.1/CHANGELOG
new/django-cacheops-7.0.2/CHANGELOG
--- old/django-cacheops-7.0.1/CHANGELOG 2023-05-09 09:41:55.000000000 +0200
+++ new/django-cacheops-7.0.2/CHANGELOG 2023-10-24 12:16:26.000000000 +0200
@@ -1,3 +1,10 @@
+7.0.2
+- fixed .aggregate()
+- fixed big memory usage during migrations
+- fixed INSIDEOUT in older redises
+- better handle model families with abstracts in them
+- allow funcy 2.0+
+
7.0.1
- made it work with Redis 6.x and older again
- handle abstract models better
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-cacheops-7.0.1/PKG-INFO
new/django-cacheops-7.0.2/PKG-INFO
--- old/django-cacheops-7.0.1/PKG-INFO 2023-05-09 09:49:47.681410600 +0200
+++ new/django-cacheops-7.0.2/PKG-INFO 2023-10-24 12:23:36.260995400 +0200
@@ -1,12 +1,11 @@
Metadata-Version: 2.1
Name: django-cacheops
-Version: 7.0.1
+Version: 7.0.2
Summary: A slick ORM cache with automatic granular event-driven invalidation
for Django.
Home-page: http://github.com/Suor/django-cacheops
Author: Alexander Schepanovski
Author-email: [email protected]
License: BSD
-Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
@@ -886,5 +885,3 @@
.. |Build Status| image::
https://github.com/Suor/django-cacheops/actions/workflows/ci.yml/badge.svg
:target:
https://github.com/Suor/django-cacheops/actions/workflows/ci.yml?query=branch%3Amaster
-
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-cacheops-7.0.1/cacheops/__init__.py
new/django-cacheops-7.0.2/cacheops/__init__.py
--- old/django-cacheops-7.0.1/cacheops/__init__.py 2023-05-09
09:42:08.000000000 +0200
+++ new/django-cacheops-7.0.2/cacheops/__init__.py 2023-10-24
12:16:49.000000000 +0200
@@ -1,4 +1,4 @@
-__version__ = '7.0.1'
+__version__ = '7.0.2'
VERSION = tuple(map(int, __version__.split('.')))
from .simple import * # noqa
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-cacheops-7.0.1/cacheops/conf.py
new/django-cacheops-7.0.2/cacheops/conf.py
--- old/django-cacheops-7.0.1/cacheops/conf.py 2023-05-05 13:51:50.000000000
+0200
+++ new/django-cacheops-7.0.2/cacheops/conf.py 2023-05-19 17:36:38.000000000
+0200
@@ -96,7 +96,7 @@
"""
Returns cacheops profile for a model
"""
- assert not model._meta.abstract, "This should be handled by caller"
+ assert not model._meta.abstract, "Can't get profile for %s" % model
# Django migrations create lots of fake models, just skip them
if model.__module__ == '__fake__':
return None
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-cacheops-7.0.1/cacheops/getset.py
new/django-cacheops-7.0.2/cacheops/getset.py
--- old/django-cacheops-7.0.1/cacheops/getset.py 2023-02-25
06:59:13.000000000 +0100
+++ new/django-cacheops-7.0.2/cacheops/getset.py 2023-10-24
12:11:51.000000000 +0200
@@ -1,6 +1,7 @@
from contextlib import contextmanager
import hashlib
import json
+import random
from .conf import settings
from .redis import redis_client, handle_connection_failure, load_script
@@ -35,6 +36,8 @@
json.dumps(schemes),
json.dumps(conj_keys),
timeout,
+ # Need to pass it from here since random inside is not seeded
in Redis pre 7.0
+ random.random(),
expected_checksum,
]
)
@@ -78,7 +81,7 @@
if None in stamps:
redis_client.unlink(key)
- return
+ return None
stamp_checksum, data = coded.split(b':', 1)
if stamp_checksum.decode() != join_stamps(stamps):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-cacheops-7.0.1/cacheops/lua/cache_thing.lua
new/django-cacheops-7.0.2/cacheops/lua/cache_thing.lua
--- old/django-cacheops-7.0.1/cacheops/lua/cache_thing.lua 2023-04-03
13:32:23.000000000 +0200
+++ new/django-cacheops-7.0.2/cacheops/lua/cache_thing.lua 2023-05-20
07:25:51.000000000 +0200
@@ -50,13 +50,13 @@
-- REDIS_7
redis.call('expire', conj_key, timeout, 'gt')
-- /REDIS_7
- -- REDIS_6
+ -- REDIS_4
local conj_ttl = redis.call('ttl', conj_key)
if conj_ttl < timeout then
-- We set conj_key life with a margin over key life to call expire
rarer
-- And add few extra seconds to be extra safe
redis.call('expire', conj_key, timeout * 2 + 10)
end
- -- /REDIS_6
+ -- /REDIS_4
end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-cacheops-7.0.1/cacheops/lua/cache_thing_insideout.lua
new/django-cacheops-7.0.2/cacheops/lua/cache_thing_insideout.lua
--- old/django-cacheops-7.0.1/cacheops/lua/cache_thing_insideout.lua
2023-04-03 13:31:30.000000000 +0200
+++ new/django-cacheops-7.0.2/cacheops/lua/cache_thing_insideout.lua
2023-05-20 08:14:06.000000000 +0200
@@ -4,7 +4,8 @@
local schemes = cjson.decode(ARGV[2])
local conj_keys = cjson.decode(ARGV[3])
local timeout = tonumber(ARGV[4])
-local expected_checksum = ARGV[5]
+local rnd = ARGV[5] -- A new value for empty stamps
+local expected_checksum = ARGV[6]
-- Ensure schemes are known
for db_table, _schemes in pairs(schemes) do
@@ -13,23 +14,31 @@
-- Fill in invalidators and collect stamps
local stamps = {}
-local rnd = tostring(math.random()) -- A new value for empty stamps
for _, conj_key in ipairs(conj_keys) do
+ -- REDIS_7
local stamp = redis.call('set', conj_key, rnd, 'nx', 'get') or rnd
+ -- /REDIS_7
+ -- REDIS_4
+ local stamp = redis.call('get', conj_key)
+ if not stamp then
+ stamp = rnd
+ redis.call('set', conj_key, rnd)
+ end
+ -- /REDIS_4
table.insert(stamps, stamp)
-- NOTE: an invalidator should live longer than any key it references.
-- So we update its ttl on every key if needed.
-- REDIS_7
redis.call('expire', conj_key, timeout, 'gt')
-- /REDIS_7
- -- REDIS_6
+ -- REDIS_4
local conj_ttl = redis.call('ttl', conj_key)
if conj_ttl < timeout then
-- We set conj_key life with a margin over key life to call expire
rarer
-- And add few extra seconds to be extra safe
redis.call('expire', conj_key, timeout * 2 + 10)
end
- -- /REDIS_6
+ -- /REDIS_4
end
-- Write data to cache along with a checksum of the stamps to see if any of
them changed
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-cacheops-7.0.1/cacheops/query.py
new/django-cacheops-7.0.2/cacheops/query.py
--- old/django-cacheops-7.0.1/cacheops/query.py 2023-05-05 13:51:50.000000000
+0200
+++ new/django-cacheops-7.0.2/cacheops/query.py 2023-10-18 11:15:33.000000000
+0200
@@ -274,14 +274,33 @@
def aggregate(self, *args, **kwargs):
if self._should_cache('aggregate'):
- # We resolve aggregates to add joins, which will affect query DNF
- qs = self._clone()
- for aggregate_expr in chain(args, kwargs.values()):
- aggregate_expr.resolve_expression(
- qs.query, allow_joins=True, reuse=None, summarize=True)
+ # Apply all aggregates the same way original .aggregate() does,
but do not perform sql.
+ # This code is mostly taken from QuerySet.aggregate().
+ normalized_kwargs = kwargs.copy()
+ for arg in args:
+ try:
+ normalized_kwargs[arg.default_alias] = arg
+ except (AttributeError, TypeError):
+ # Let Django raise a proper error
+ return self._no_monkey.aggregate(*args, **kwargs)
+
+ # Simulate Query.get_aggregation() preparations, this adds proper
joins to qs.query
+ if not normalized_kwargs:
+ return {}
- # Use resulting qs as a ref
- return cached_as(qs)(lambda: self._no_monkey.aggregate(self,
*args, **kwargs))()
+ qs = self._clone()
+ aggregates = {}
+ for alias, aggregate_expr in normalized_kwargs.items():
+ aggregate = aggregate_expr.resolve_expression(
+ qs.query, allow_joins=True, reuse=None, summarize=True
+ )
+ if not aggregate.contains_aggregate:
+ raise TypeError("%s is not an aggregate expression" %
alias)
+ aggregates[alias] = aggregate
+
+ # Use resulting qs as a ref, aggregates still contain names, etc
+ func = lambda: self._no_monkey.aggregate(self, *args, **kwargs)
+ return cached_as(qs, extra=aggregates)(func)()
else:
return self._no_monkey.aggregate(self, *args, **kwargs)
@@ -397,8 +416,9 @@
def contribute_to_class(self, cls, name):
self._no_monkey.contribute_to_class(self, cls, name)
# NOTE: we check it here rather then inside _install_cacheops()
- # because we don't want @once_per() to hold refs to all of them.
- if family_has_profile(cls):
+ # because we don't want @once_per() and family_has_profile()
memory to hold refs.
+ # Otherwise, temporary classes made for migrations might hoard
lots of memory.
+ if cls.__module__ != '__fake__' and family_has_profile(cls):
self._install_cacheops(cls)
@skip_on_no_invalidation
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-cacheops-7.0.1/cacheops/redis.py
new/django-cacheops-7.0.2/cacheops/redis.py
--- old/django-cacheops-7.0.1/cacheops/redis.py 2023-04-04 12:55:06.000000000
+0200
+++ new/django-cacheops-7.0.2/cacheops/redis.py 2023-05-20 08:14:38.000000000
+0200
@@ -63,7 +63,7 @@
with open(filename) as f:
code = f.read()
if is_redis_7():
- code = re.sub(r'REDIS_6.*?/REDIS_6', '', code, flags=re.S)
+ code = re.sub(r'REDIS_4.*?/REDIS_4', '', code, flags=re.S)
else:
code = re.sub(r'REDIS_7.*?/REDIS_7', '', code, flags=re.S)
return redis_client.register_script(code)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-cacheops-7.0.1/cacheops/utils.py
new/django-cacheops-7.0.2/cacheops/utils.py
--- old/django-cacheops-7.0.1/cacheops/utils.py 2023-05-05 13:51:50.000000000
+0200
+++ new/django-cacheops-7.0.2/cacheops/utils.py 2023-05-19 17:43:23.000000000
+0200
@@ -1,7 +1,7 @@
import re
import json
import inspect
-from funcy import memoize, compose, wraps, any, any_fn, select_values, lmapcat
+from funcy import memoize, compose, wraps, any, any_fn, select_values, mapcat
from django.db import models
from django.http import HttpRequest
@@ -9,10 +9,6 @@
from .conf import model_profile
-def get_table_model(model):
- return next((b for b in model.__mro__ if issubclass(b, models.Model) and b
is not models.Model
- and not b._meta.proxy and not b._meta.abstract), None)
-
def model_family(model):
"""
The family is models sharing a database table, events on one should affect
each other.
@@ -20,14 +16,19 @@
We simply collect a list of all proxy models, including subclasess,
superclasses and siblings.
Two descendants of an abstract model are not family - they cannot affect
each other.
"""
- def class_tree(cls):
- return [cls] + lmapcat(class_tree, cls.__subclasses__())
-
- # NOTE: we also list multitable submodels here, we just don't care.
- # Cacheops doesn't support them anyway.
- table_model = get_table_model(model)
- return class_tree(table_model) if table_model else []
+ if model._meta.abstract: # No table - no family
+ return set()
+ @memoize
+ def class_tree(cls):
+ # NOTE: we also list multitable submodels here, we just don't care.
+ # Cacheops doesn't support them anyway.
+ return {cls} | set(mapcat(class_tree, cls.__subclasses__()))
+
+ table_bases = {b for b in model.__mro__ if issubclass(b, models.Model) and
b is not models.Model
+ and not b._meta.proxy and not b._meta.abstract}
+ family = set(mapcat(class_tree, table_bases))
+ return {cls for cls in family if not cls._meta.abstract}
@memoize
def family_has_profile(cls):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-cacheops-7.0.1/django_cacheops.egg-info/PKG-INFO
new/django-cacheops-7.0.2/django_cacheops.egg-info/PKG-INFO
--- old/django-cacheops-7.0.1/django_cacheops.egg-info/PKG-INFO 2023-05-09
09:49:47.000000000 +0200
+++ new/django-cacheops-7.0.2/django_cacheops.egg-info/PKG-INFO 2023-10-24
12:23:36.000000000 +0200
@@ -1,12 +1,11 @@
Metadata-Version: 2.1
Name: django-cacheops
-Version: 7.0.1
+Version: 7.0.2
Summary: A slick ORM cache with automatic granular event-driven invalidation
for Django.
Home-page: http://github.com/Suor/django-cacheops
Author: Alexander Schepanovski
Author-email: [email protected]
License: BSD
-Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
@@ -886,5 +885,3 @@
.. |Build Status| image::
https://github.com/Suor/django-cacheops/actions/workflows/ci.yml/badge.svg
:target:
https://github.com/Suor/django-cacheops/actions/workflows/ci.yml?query=branch%3Amaster
-
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-cacheops-7.0.1/django_cacheops.egg-info/requires.txt
new/django-cacheops-7.0.2/django_cacheops.egg-info/requires.txt
--- old/django-cacheops-7.0.1/django_cacheops.egg-info/requires.txt
2023-05-09 09:49:47.000000000 +0200
+++ new/django-cacheops-7.0.2/django_cacheops.egg-info/requires.txt
2023-10-24 12:23:36.000000000 +0200
@@ -1,3 +1,3 @@
django>=3.2
redis>=3.0.0
-funcy<2.0,>=1.8
+funcy<3.0,>=1.8
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-cacheops-7.0.1/requirements-test.txt
new/django-cacheops-7.0.2/requirements-test.txt
--- old/django-cacheops-7.0.1/requirements-test.txt 2023-03-11
10:05:33.000000000 +0100
+++ new/django-cacheops-7.0.2/requirements-test.txt 2023-05-11
08:42:49.000000000 +0200
@@ -2,7 +2,7 @@
pytest-django==4.5.2
django>=3.2
redis>=3.0.0
-funcy>=1.8,<2.0
+funcy>=1.8,<3.0
before_after==1.0.0
jinja2>=2.10
dill
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-cacheops-7.0.1/setup.py
new/django-cacheops-7.0.2/setup.py
--- old/django-cacheops-7.0.1/setup.py 2023-05-09 09:42:03.000000000 +0200
+++ new/django-cacheops-7.0.2/setup.py 2023-10-24 12:16:36.000000000 +0200
@@ -7,7 +7,7 @@
setup(
name='django-cacheops',
- version='7.0.1',
+ version='7.0.2',
author='Alexander Schepanovski',
author_email='[email protected]',
@@ -26,7 +26,7 @@
install_requires=[
'django>=3.2',
'redis>=3.0.0',
- 'funcy>=1.8,<2.0',
+ 'funcy>=1.8,<3.0',
],
classifiers=[
'Development Status :: 5 - Production/Stable',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-cacheops-7.0.1/tests/models.py
new/django-cacheops-7.0.2/tests/models.py
--- old/django-cacheops-7.0.1/tests/models.py 2023-05-05 13:51:50.000000000
+0200
+++ new/django-cacheops-7.0.2/tests/models.py 2023-05-20 07:03:30.000000000
+0200
@@ -309,6 +309,44 @@
name = models.CharField(max_length=255)
+# Abstract models
class Abs(models.Model):
class Meta:
abstract = True
+
+class Concrete1(Abs):
+ pass
+
+class AbsChild(Abs):
+ class Meta:
+ abstract = True
+
+class Concrete2(AbsChild):
+ pass
+
+class NoProfile(models.Model):
+ title = models.CharField(max_length=128)
+
+class NoProfileProxy(NoProfile):
+ class Meta:
+ proxy = True
+
+class AbsNoProfile(NoProfile):
+ class Meta:
+ abstract = True
+
+class NoProfileChild(AbsNoProfile):
+ pass
+
+
+class ParentId(models.Model):
+ pass
+
+class ParentStr(models.Model):
+ name = models.CharField(max_length=128, primary_key=True)
+
+class Mess(ParentId, ParentStr):
+ pass
+
+class MessChild(Mess):
+ pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-cacheops-7.0.1/tests/settings.py
new/django-cacheops-7.0.2/tests/settings.py
--- old/django-cacheops-7.0.1/tests/settings.py 2023-05-03 09:01:43.000000000
+0200
+++ new/django-cacheops-7.0.2/tests/settings.py 2023-05-17 13:55:39.000000000
+0200
@@ -111,6 +111,7 @@
'tests.*': {},
'tests.noncachedvideoproxy': None,
'tests.noncachedmedia': None,
+ 'tests.noprofile': None,
'auth.*': {},
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-cacheops-7.0.1/tests/test_extras.py
new/django-cacheops-7.0.2/tests/test_extras.py
--- old/django-cacheops-7.0.1/tests/test_extras.py 2023-05-05
13:51:50.000000000 +0200
+++ new/django-cacheops-7.0.2/tests/test_extras.py 2023-05-20
07:03:41.000000000 +0200
@@ -6,7 +6,7 @@
from cacheops.signals import cache_read, cache_invalidated
from .utils import BaseTestCase, make_inc
-from .models import Post, Category, Local, DbAgnostic, DbBinded, Abs
+from .models import Post, Category, Local, DbAgnostic, DbBinded
class SettingsTests(TestCase):
@@ -185,7 +185,23 @@
list(DbBinded.objects.cache().using('slave'))
-def test_abstract_family():
+def test_model_family():
from cacheops.utils import model_family
+ from .models import Abs, Concrete1, AbsChild, Concrete2
+ from .models import NoProfile, NoProfileProxy, AbsNoProfile, NoProfileChild
+ from .models import ParentId, ParentStr, Mess, MessChild
+
+ # Abstract models do not have family, children of an abstract model are
not a family
+ assert model_family(Abs) == set()
+ assert model_family(Concrete1) == {Concrete1}
+ assert model_family(AbsChild) == set()
+ assert model_family(Concrete2) == {Concrete2}
+
+ # Everything in but an abstract model
+ assert model_family(NoProfile) == {NoProfile, NoProfileProxy,
NoProfileChild}
+ assert model_family(NoProfileProxy) == {NoProfile, NoProfileProxy,
NoProfileChild}
+ assert model_family(AbsNoProfile) == set()
+ assert model_family(NoProfileChild) == {NoProfile, NoProfileProxy,
NoProfileChild}
- assert model_family(Abs) == []
+ # The worst of multiple inheritance
+ assert model_family(Mess) == {Mess, MessChild, ParentId, ParentStr}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-cacheops-7.0.1/tests/tests.py
new/django-cacheops-7.0.2/tests/tests.py
--- old/django-cacheops-7.0.1/tests/tests.py 2023-05-05 13:51:50.000000000
+0200
+++ new/django-cacheops-7.0.2/tests/tests.py 2023-05-20 08:14:46.000000000
+0200
@@ -10,7 +10,7 @@
from django.test import override_settings
from django.test.client import RequestFactory
from django.template import Context, Template
-from django.db.models import F, Count, OuterRef, Sum, Subquery, Exists
+from django.db.models import F, Count, Max, OuterRef, Sum, Subquery, Exists, Q
from django.db.models.expressions import RawSQL
from cacheops import invalidate_model, invalidate_obj, \
@@ -794,6 +794,16 @@
with self.assertNumQueries(0):
qs.aggregate(posts_count=Count('posts'))
+ def test_new_alias(self):
+ qs = Post.objects.cache()
+ assert qs.aggregate(max=Max('category')) == {'max': 3}
+ assert qs.aggregate(cat=Max('category')) == {'cat': 3}
+
+ def test_filter(self):
+ qs = Post.objects.cache()
+ assert qs.aggregate(cnt=Count('category', filter=Q(category__gt=1)))
== {'cnt': 2}
+ assert qs.aggregate(cnt=Count('category', filter=Q(category__lt=3)))
== {'cnt': 1}
+
class M2MTests(BaseTestCase):
brand_cls = Brand