Просмотр исходного кода

Merge pull request #1149 from rafalp/remove-global-state-threadstore

Remove misago.core.threadstore
Rafał Pitoń 6 лет назад
Родитель
Сommit
fa50746012

+ 0 - 1
devproject/settings.py

@@ -232,7 +232,6 @@ MIDDLEWARE = [
     'misago.users.middleware.OnlineTrackerMiddleware',
     'misago.users.middleware.OnlineTrackerMiddleware',
     'misago.admin.middleware.AdminAuthMiddleware',
     'misago.admin.middleware.AdminAuthMiddleware',
     'misago.threads.middleware.UnreadThreadsCountMiddleware',
     'misago.threads.middleware.UnreadThreadsCountMiddleware',
-    'misago.core.middleware.ThreadStoreMiddleware',
 ]
 ]
 
 
 ROOT_URLCONF = 'devproject.urls'
 ROOT_URLCONF = 'devproject.urls'

+ 1 - 9
misago/acl/migrations/0002_acl_version_tracker.py

@@ -1,11 +1,6 @@
 from django.db import migrations
 from django.db import migrations
 
 
 
 
-def register_acl_version_tracker(apps, schema_editor):
-    from misago.core.migrationutils import cachebuster_register_cache
-    cachebuster_register_cache(apps, "misago_acl")
-
-
 class Migration(migrations.Migration):
 class Migration(migrations.Migration):
     """Superseded by 0004"""
     """Superseded by 0004"""
 
 
@@ -14,7 +9,4 @@ class Migration(migrations.Migration):
         ('misago_core', '0001_initial'),
         ('misago_core', '0001_initial'),
     ]
     ]
 
 
-    operations = [
-        # FIXME: remove this operation
-        migrations.RunPython(register_acl_version_tracker),
-    ]
+    operations = []

+ 17 - 18
misago/admin/views/generic/list.py

@@ -116,15 +116,15 @@ class ListView(AdminView):
                 # So address ball contains copy-friendly link
                 # So address ball contains copy-friendly link
                 refresh_querystring = True
                 refresh_querystring = True
 
 
-        SearchForm = self.get_search_form(request)
-        if SearchForm:
-            filtering_methods = self.get_filtering_methods(request)
+        search_form = self.get_search_form(request)
+        if search_form:
+            filtering_methods = self.get_filtering_methods(request, search_form)
             active_filters = self.get_filtering_method_to_use(filtering_methods)
             active_filters = self.get_filtering_method_to_use(filtering_methods)
             if request.GET.get('clear_filters'):
             if request.GET.get('clear_filters'):
                 # Clear filters from querystring
                 # Clear filters from querystring
                 request.session.pop(self.filters_session_key, None)
                 request.session.pop(self.filters_session_key, None)
                 active_filters = {}
                 active_filters = {}
-            self.apply_filtering_on_context(context, active_filters, SearchForm)
+            self.apply_filtering_on_context(context, active_filters, search_form)
 
 
             if (filtering_methods['GET'] and
             if (filtering_methods['GET'] and
                     filtering_methods['GET'] != filtering_methods['session']):
                     filtering_methods['GET'] != filtering_methods['session']):
@@ -181,12 +181,23 @@ class ListView(AdminView):
     def filters_session_key(self):
     def filters_session_key(self):
         return 'misago_admin_%s_filters' % self.root_link
         return 'misago_admin_%s_filters' % self.root_link
 
 
-    def get_filters_from_GET(self, search_form, request):
+    def get_filtering_methods(self, request, search_form):
+        methods = {
+            'GET': self.get_filters_from_GET(request, search_form),
+            'session': self.get_filters_from_session(request, search_form),
+        }
+
+        if request.GET.get('set_filters'):
+            methods['session'] = {}
+
+        return methods
+
+    def get_filters_from_GET(self, request, search_form):
         form = search_form(request.GET)
         form = search_form(request.GET)
         form.is_valid()
         form.is_valid()
         return self.clean_filtering_data(form.cleaned_data)
         return self.clean_filtering_data(form.cleaned_data)
 
 
-    def get_filters_from_session(self, search_form, request):
+    def get_filters_from_session(self, request, search_form):
         session_filters = request.session.get(self.filters_session_key, {})
         session_filters = request.session.get(self.filters_session_key, {})
         form = search_form(session_filters)
         form = search_form(session_filters)
         form.is_valid()
         form.is_valid()
@@ -198,18 +209,6 @@ class ListView(AdminView):
                 del data[key]
                 del data[key]
         return data
         return data
 
 
-    def get_filtering_methods(self, request):
-        SearchForm = self.get_search_form(request)
-        methods = {
-            'GET': self.get_filters_from_GET(SearchForm, request),
-            'session': self.get_filters_from_session(SearchForm, request),
-        }
-
-        if request.GET.get('set_filters'):
-            methods['session'] = {}
-
-        return methods
-
     def get_filtering_method_to_use(self, methods):
     def get_filtering_method_to_use(self, methods):
         for method in ('GET', 'session'):
         for method in ('GET', 'session'):
             if methods.get(method):
             if methods.get(method):

+ 4 - 3
misago/categories/tests/test_category_model.py

@@ -1,11 +1,12 @@
+from django.test import TestCase
+
 from misago.categories import THREADS_ROOT_NAME
 from misago.categories import THREADS_ROOT_NAME
 from misago.categories.models import Category
 from misago.categories.models import Category
-from misago.core.testutils import MisagoTestCase
 from misago.threads import testutils
 from misago.threads import testutils
 from misago.threads.threadtypes import trees_map
 from misago.threads.threadtypes import trees_map
 
 
 
 
-class CategoryManagerTests(MisagoTestCase):
+class CategoryManagerTests(TestCase):
     def test_private_threads(self):
     def test_private_threads(self):
         """private_threads returns private threads category"""
         """private_threads returns private threads category"""
         category = Category.objects.private_threads()
         category = Category.objects.private_threads()
@@ -45,7 +46,7 @@ class CategoryManagerTests(MisagoTestCase):
                 self.assertNotIn(category.id, test_dict)
                 self.assertNotIn(category.id, test_dict)
 
 
 
 
-class CategoryModelTests(MisagoTestCase):
+class CategoryModelTests(TestCase):
     def setUp(self):
     def setUp(self):
         super().setUp()
         super().setUp()
 
 

+ 0 - 3
misago/categories/tests/test_utils.py

@@ -2,7 +2,6 @@ from misago.acl.useracl import get_user_acl
 from misago.categories.models import Category
 from misago.categories.models import Category
 from misago.categories.utils import get_categories_tree, get_category_path
 from misago.categories.utils import get_categories_tree, get_category_path
 from misago.conftest import get_cache_versions
 from misago.conftest import get_cache_versions
-from misago.core import threadstore
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
 cache_versions = get_cache_versions()
 cache_versions = get_cache_versions()
@@ -33,9 +32,7 @@ class CategoriesUtilsTests(AuthenticatedUserTestCase):
         Category E
         Category E
           + Subcategory F
           + Subcategory F
         """
         """
-
         super().setUp()
         super().setUp()
-        threadstore.clear()
 
 
         self.root = Category.objects.root_category()
         self.root = Category.objects.root_category()
         self.first_category = Category.objects.get(slug='first-category')
         self.first_category = Category.objects.get(slug='first-category')

+ 0 - 4
misago/conf/dynamicsettings.py

@@ -45,10 +45,6 @@ class DynamicSettings:
         cls._overrides = {}
         cls._overrides = {}
 
 
 
 
-def get_cache_name(cache_versions):
-    return "%s_%s" % (SETTINGS_CACHE, cache_versions[SETTINGS_CACHE])
-
-
 def get_settings_from_db():
 def get_settings_from_db():
     settings = {}
     settings = {}
     for setting in Setting.objects.iterator():
     for setting in Setting.objects.iterator():

+ 3 - 9
misago/conf/tests/test_context_processors.py

@@ -2,26 +2,20 @@ from unittest.mock import Mock
 
 
 from django.test import TestCase
 from django.test import TestCase
 
 
-from misago.cache.versions import get_cache_versions
-from misago.core import threadstore
+from misago.conftest import get_cache_versions
 
 
 from misago.conf.context_processors import conf
 from misago.conf.context_processors import conf
 from misago.conf.dynamicsettings import DynamicSettings
 from misago.conf.dynamicsettings import DynamicSettings
 
 
 
 
 class ContextProcessorsTests(TestCase):
 class ContextProcessorsTests(TestCase):
-    def tearDown(self):
-        threadstore.clear()
-
-    def test_db_settings(self):
-        """DBSettings are exposed to templates"""
+    def test_request_settings_are_included_in_template_context(self):
         cache_versions = get_cache_versions()
         cache_versions = get_cache_versions()
         mock_request = Mock(settings=DynamicSettings(cache_versions))
         mock_request = Mock(settings=DynamicSettings(cache_versions))
         context_settings = conf(mock_request)['settings']
         context_settings = conf(mock_request)['settings']
         assert context_settings == mock_request.settings
         assert context_settings == mock_request.settings
 
 
-    def test_preload_settings(self):
-        """site configuration is preloaded by middleware"""
+    def test_settings_are_included_in_frontend_context(self):
         response = self.client.get('/')
         response = self.client.get('/')
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertContains(response, '"SETTINGS": {"')
         self.assertContains(response, '"SETTINGS": {"')

+ 0 - 4
misago/conf/tests/test_migrationutils.py

@@ -3,7 +3,6 @@ from django.test import TestCase
 
 
 from misago.conf import migrationutils
 from misago.conf import migrationutils
 from misago.conf.models import SettingsGroup
 from misago.conf.models import SettingsGroup
-from misago.core import threadstore
 
 
 
 
 class DBConfMigrationUtilsTests(TestCase):
 class DBConfMigrationUtilsTests(TestCase):
@@ -36,9 +35,6 @@ class DBConfMigrationUtilsTests(TestCase):
         migrationutils.migrate_settings_group(apps, self.test_group)
         migrationutils.migrate_settings_group(apps, self.test_group)
         self.groups_count = SettingsGroup.objects.count()
         self.groups_count = SettingsGroup.objects.count()
 
 
-    def tearDown(self):
-        threadstore.clear()
-
     def test_get_custom_group_and_settings(self):
     def test_get_custom_group_and_settings(self):
         """tests setup created settings group"""
         """tests setup created settings group"""
         custom_group = migrationutils.get_group(
         custom_group = migrationutils.get_group(

+ 0 - 104
misago/core/cachebuster.py

@@ -1,104 +0,0 @@
-from django.db.models import F
-
-from . import threadstore
-
-
-CACHE_KEY = 'misago_cachebuster'
-
-
-class CacheBusterController(object):
-    def register_cache(self, cache):
-        from .models import CacheVersion
-        CacheVersion.objects.create(cache=cache)
-
-    def unregister_cache(self, cache):
-        from .models import CacheVersion
-
-        try:
-            cache = CacheVersion.objects.get(cache=cache)
-            cache.delete()
-        except CacheVersion.DoesNotExist:
-            raise ValueError('Cache "%s" is not registered' % cache)
-
-    @property
-    def cache(self):
-        return self.read_threadstore()
-
-    def read_threadstore(self):
-        data = threadstore.get(CACHE_KEY, 'nada')
-        if data == 'nada':
-            data = self.read_cache()
-            threadstore.set(CACHE_KEY, data)
-        return data
-
-    def read_cache(self):
-        from .cache import cache as default_cache
-
-        data = default_cache.get(CACHE_KEY, 'nada')
-        if data == 'nada':
-            data = self.read_db()
-            default_cache.set(CACHE_KEY, data)
-        return data
-
-    def read_db(self):
-        from .models import CacheVersion
-
-        data = {}
-        for cache_version in CacheVersion.objects.iterator():
-            data[cache_version.cache] = cache_version.version
-        return data
-
-    def get_cache_version(self, cache):
-        try:
-            return self.cache[cache]
-        except KeyError:
-            raise ValueError('Cache "%s" is not registered' % cache)
-
-    def is_cache_valid(self, cache, version):
-        try:
-            return self.cache[cache] == version
-        except KeyError:
-            raise ValueError('Cache "%s" is not registered' % cache)
-
-    def invalidate_cache(self, cache):
-        from .cache import cache as default_cache
-        from .models import CacheVersion
-
-        self.cache[cache] += 1
-        CacheVersion.objects.filter(cache=cache).update(version=F('version') + 1)
-        default_cache.delete(CACHE_KEY)
-
-    def invalidate_all(self):
-        from .cache import cache as default_cache
-        from .models import CacheVersion
-
-        CacheVersion.objects.update(version=F('version') + 1)
-        default_cache.delete(CACHE_KEY)
-
-
-_controller = CacheBusterController()
-
-
-# Expose controller API
-def register(cache_name):
-    _controller.register_cache(cache_name)
-
-
-def unregister(cache_name):
-    _controller.unregister_cache(cache_name)
-
-
-def get_version(cache_name):
-    return _controller.get_cache_version(cache_name)
-
-
-def is_valid(cache_name, version):
-    return _controller.is_cache_valid(cache_name, version)
-
-
-def invalidate(cache_name):
-    _controller.invalidate_cache(cache_name)
-
-
-def invalidate_all():
-    _controller.invalidate_all()

+ 1 - 7
misago/core/middleware.py

@@ -1,6 +1,6 @@
 from django.utils.deprecation import MiddlewareMixin
 from django.utils.deprecation import MiddlewareMixin
 
 
-from misago.core import exceptionhandler, threadstore
+from misago.core import exceptionhandler
 from misago.core.utils import is_request_to_misago
 from misago.core.utils import is_request_to_misago
 
 
 
 
@@ -19,9 +19,3 @@ class FrontendContextMiddleware(MiddlewareMixin):
     def process_request(self, request):
     def process_request(self, request):
         request.include_frontend_context = True
         request.include_frontend_context = True
         request.frontend_context = {}
         request.frontend_context = {}
-
-
-class ThreadStoreMiddleware(MiddlewareMixin):
-    def process_response(self, request, response):
-        threadstore.clear()
-        return response

+ 0 - 24
misago/core/migrationutils.py

@@ -1,24 +0,0 @@
-from .cache import cache as default_cache
-from .cachebuster import CACHE_KEY
-
-
-def _CacheVersion(apps):
-    return apps.get_model('misago_core', 'CacheVersion')
-
-
-def cachebuster_register_cache(apps, cache):
-    _CacheVersion(apps).objects.create(cache=cache)
-
-
-def cachebuster_unregister_cache(apps, cache):
-    CacheVersion = _CacheVersion(apps)
-
-    try:
-        cache = CacheVersion.objects.get(cache=cache)
-        cache.delete()
-    except CacheVersion.DoesNotExist:
-        raise ValueError('Cache "%s" is not registered' % cache)
-
-
-def delete_cachebuster_cache():
-    default_cache.delete(CACHE_KEY)

+ 0 - 73
misago/core/tests/test_cachebuster.py

@@ -1,73 +0,0 @@
-from misago.core import cachebuster
-from misago.core.models import CacheVersion
-from misago.core.testutils import MisagoTestCase
-
-
-class CacheBusterTests(MisagoTestCase):
-    def test_register_unregister_cache(self):
-        """register and unregister adds/removes cache"""
-        test_cache_name = 'eric_the_fish'
-        with self.assertRaises(CacheVersion.DoesNotExist):
-            CacheVersion.objects.get(cache=test_cache_name)
-
-        cachebuster.register(test_cache_name)
-        CacheVersion.objects.get(cache=test_cache_name)
-
-        cachebuster.unregister(test_cache_name)
-        with self.assertRaises(CacheVersion.DoesNotExist):
-            CacheVersion.objects.get(cache=test_cache_name)
-
-
-class CacheBusterCacheTests(MisagoTestCase):
-    def setUp(self):
-        super().setUp()
-
-        self.cache_name = 'eric_the_fish'
-        cachebuster.register(self.cache_name)
-
-    def test_cache_validation(self):
-        """cache correctly validates"""
-        version = cachebuster.get_version(self.cache_name)
-        self.assertEqual(version, 0)
-
-        db_version = CacheVersion.objects.get(cache=self.cache_name).version
-        self.assertEqual(db_version, 0)
-
-        self.assertEqual(db_version, version)
-        self.assertTrue(cachebuster.is_valid(self.cache_name, version))
-        self.assertTrue(cachebuster.is_valid(self.cache_name, db_version))
-
-    def test_cache_invalidation(self):
-        """invalidate has increased valid version number"""
-        db_version = CacheVersion.objects.get(cache=self.cache_name).version
-        cachebuster.invalidate(self.cache_name)
-
-        new_version = cachebuster.get_version(self.cache_name)
-        new_db_version = CacheVersion.objects.get(cache=self.cache_name)
-        new_db_version = new_db_version.version
-
-        self.assertEqual(new_version, 1)
-        self.assertEqual(new_db_version, 1)
-        self.assertEqual(new_version, new_db_version)
-        self.assertFalse(cachebuster.is_valid(self.cache_name, db_version))
-        self.assertTrue(cachebuster.is_valid(self.cache_name, new_db_version))
-
-    def test_cache_invalidation_all(self):
-        """invalidate_all has increased valid version number"""
-        cache_a = "eric_the_halibut"
-        cache_b = "eric_the_crab"
-        cache_c = "eric_the_lion"
-
-        cachebuster.register(cache_a)
-        cachebuster.register(cache_b)
-        cachebuster.register(cache_c)
-
-        cachebuster.invalidate_all()
-
-        new_version_a = CacheVersion.objects.get(cache=cache_a).version
-        new_version_b = CacheVersion.objects.get(cache=cache_b).version
-        new_version_c = CacheVersion.objects.get(cache=cache_c).version
-
-        self.assertEqual(new_version_a, 1)
-        self.assertEqual(new_version_b, 1)
-        self.assertEqual(new_version_c, 1)

+ 0 - 26
misago/core/tests/test_migrationutils.py

@@ -1,26 +0,0 @@
-from django.apps import apps
-from django.test import TestCase
-
-from misago.core import migrationutils
-from misago.core.models import CacheVersion
-
-
-class CacheBusterUtilsTests(TestCase):
-    def test_cachebuster_register_cache(self):
-        """cachebuster_register_cache registers cache on migration successfully"""
-        cache_name = 'eric_licenses'
-        migrationutils.cachebuster_register_cache(apps, cache_name)
-        CacheVersion.objects.get(cache=cache_name)
-
-    def test_cachebuster_unregister_cache(self):
-        """cachebuster_unregister_cache removes cache on migration successfully"""
-        cache_name = 'eric_licenses'
-        migrationutils.cachebuster_register_cache(apps, cache_name)
-        CacheVersion.objects.get(cache=cache_name)
-
-        migrationutils.cachebuster_unregister_cache(apps, cache_name)
-        with self.assertRaises(CacheVersion.DoesNotExist):
-            CacheVersion.objects.get(cache=cache_name)
-
-        with self.assertRaises(ValueError):
-            migrationutils.cachebuster_unregister_cache(apps, cache_name)

+ 0 - 41
misago/core/tests/test_threadstore.py

@@ -1,41 +0,0 @@
-from django.test import TestCase
-from django.test.client import RequestFactory
-from django.urls import reverse
-
-from misago.core import threadstore
-from misago.core.middleware import ThreadStoreMiddleware
-
-
-class ThreadStoreTests(TestCase):
-    def setUp(self):
-        threadstore.clear()
-
-    def test_set_get_value(self):
-        """It's possible to set and get value from threadstore"""
-        self.assertEqual(threadstore.get('knights_say'), None)
-
-        returned_value = threadstore.set('knights_say', 'Ni!')
-        self.assertEqual(returned_value, 'Ni!')
-        self.assertEqual(threadstore.get('knights_say'), 'Ni!')
-
-    def test_clear_store(self):
-        """clear cleared threadstore"""
-        self.assertEqual(threadstore.get('the_fish'), None)
-        threadstore.set('the_fish', 'Eric')
-        self.assertEqual(threadstore.get('the_fish'), 'Eric')
-        threadstore.clear()
-        self.assertEqual(threadstore.get('the_fish'), None)
-
-
-class ThreadStoreMiddlewareTests(TestCase):
-    def setUp(self):
-        self.request = RequestFactory().get(reverse('misago:index'))
-
-    def test_middleware_clears_store_on_response_exception(self):
-        """Middleware cleared store on response"""
-
-        threadstore.set('any_chesse', 'Nope')
-        middleware = ThreadStoreMiddleware()
-        response = middleware.process_response(self.request, 'FakeResponse')
-        self.assertEqual(response, 'FakeResponse')
-        self.assertEqual(threadstore.get('any_chesse'), None)

+ 0 - 20
misago/core/testutils.py

@@ -1,20 +0,0 @@
-from django.test import TestCase
-
-from . import threadstore
-from .cache import cache
-
-
-class MisagoTestCase(TestCase):
-    """TestCase class that empties global state before and after each test"""
-
-    def clear_state(self):
-        cache.clear()
-        threadstore.clear()
-
-    def setUp(self):
-        super().setUp()
-        self.clear_state()
-
-    def tearDown(self):
-        self.clear_state()
-        super().tearDown()

+ 0 - 17
misago/core/threadstore.py

@@ -1,17 +0,0 @@
-from threading import local
-
-
-_thread_local = local()
-
-
-def get(key, default=None):
-    return _thread_local.__dict__.get(key, default)
-
-
-def set(key, value):
-    _thread_local.__dict__[key] = value
-    return _thread_local.__dict__[key]
-
-
-def clear():
-    _thread_local.__dict__.clear()

+ 0 - 2
misago/threads/tests/test_threadslists.py

@@ -146,8 +146,6 @@ class ThreadsListTestCase(AuthenticatedUserTestCase):
 
 
         self.category_f = Category.objects.get(slug='category-f')
         self.category_f = Category.objects.get(slug='category-f')
 
 
-        self.clear_state()
-
         Category.objects.partial_rebuild(self.root.tree_id)
         Category.objects.partial_rebuild(self.root.tree_id)
 
 
         self.root = Category.objects.root_category()
         self.root = Category.objects.root_category()

+ 4 - 6
misago/threads/tests/test_utils.py

@@ -1,10 +1,11 @@
+from django.test import TestCase
+
 from misago.categories.models import Category
 from misago.categories.models import Category
-from misago.core.testutils import MisagoTestCase
 from misago.threads import testutils
 from misago.threads import testutils
 from misago.threads.utils import add_categories_to_items, get_thread_id_from_url
 from misago.threads.utils import add_categories_to_items, get_thread_id_from_url
 
 
 
 
-class AddCategoriesToItemsTests(MisagoTestCase):
+class AddCategoriesToItemsTests(TestCase):
     def setUp(self):
     def setUp(self):
         """
         """
         Create categories tree for test cases:
         Create categories tree for test cases:
@@ -19,7 +20,6 @@ class AddCategoriesToItemsTests(MisagoTestCase):
         Category E
         Category E
           + Subcategory F
           + Subcategory F
         """
         """
-
         super().setUp()
         super().setUp()
 
 
         self.root = Category.objects.root_category()
         self.root = Category.objects.root_category()
@@ -90,8 +90,6 @@ class AddCategoriesToItemsTests(MisagoTestCase):
             save=True,
             save=True,
         )
         )
 
 
-        self.clear_state()
-
         Category.objects.partial_rebuild(self.root.tree_id)
         Category.objects.partial_rebuild(self.root.tree_id)
 
 
         self.root = Category.objects.root_category()
         self.root = Category.objects.root_category()
@@ -176,7 +174,7 @@ class MockRequest(object):
         return self.scheme == 'https'
         return self.scheme == 'https'
 
 
 
 
-class GetThreadIdFromUrlTests(MisagoTestCase):
+class GetThreadIdFromUrlTests(TestCase):
     def test_get_thread_id_from_valid_urls(self):
     def test_get_thread_id_from_valid_urls(self):
         """get_thread_id_from_url extracts thread pk from valid urls"""
         """get_thread_id_from_url extracts thread pk from valid urls"""
         TEST_CASES = [
         TEST_CASES = [

+ 10 - 19
misago/users/forms/admin.py

@@ -7,8 +7,6 @@ from django.utils.translation import ngettext
 
 
 from misago.acl.models import Role
 from misago.acl.models import Role
 from misago.admin.forms import IsoDateTimeField, YesNoSwitch
 from misago.admin.forms import IsoDateTimeField, YesNoSwitch
-from misago.conf import settings
-from misago.core import threadstore
 from misago.core.validators import validate_sluggable
 from misago.core.validators import validate_sluggable
 
 
 from misago.users.models import Ban, DataDownload, Rank
 from misago.users.models import Ban, DataDownload, Rank
@@ -308,7 +306,7 @@ def EditUserFormFactory(FormType, instance, add_is_active_fields=False, add_admi
     return FormType
     return FormType
 
 
 
 
-class SearchUsersFormBase(forms.Form):
+class BaseSearchUsersForm(forms.Form):
     username = forms.CharField(label=_("Username starts with"), required=False)
     username = forms.CharField(label=_("Username starts with"), required=False)
     email = forms.CharField(label=_("E-mail starts with"), required=False)
     email = forms.CharField(label=_("E-mail starts with"), required=False)
     profilefields = forms.CharField(label=_("Profile fields contain"), required=False)
     profilefields = forms.CharField(label=_("Profile fields contain"), required=False)
@@ -349,25 +347,19 @@ class SearchUsersFormBase(forms.Form):
         return queryset
         return queryset
 
 
 
 
-def SearchUsersForm(*args, **kwargs):
+def create_search_users_form():
     """
     """
     Factory that uses cache for ranks and roles,
     Factory that uses cache for ranks and roles,
     and makes those ranks and roles typed choice fields that play nice
     and makes those ranks and roles typed choice fields that play nice
     with passing values via GET
     with passing values via GET
     """
     """
-    ranks_choices = threadstore.get('misago_admin_ranks_choices', 'nada')
-    if ranks_choices == 'nada':
-        ranks_choices = [('', _("All ranks"))]
-        for rank in Rank.objects.order_by('name').iterator():
-            ranks_choices.append((rank.pk, rank.name))
-        threadstore.set('misago_admin_ranks_choices', ranks_choices)
-
-    roles_choices = threadstore.get('misago_admin_roles_choices', 'nada')
-    if roles_choices == 'nada':
-        roles_choices = [('', _("All roles"))]
-        for role in Role.objects.order_by('name').iterator():
-            roles_choices.append((role.pk, role.name))
-        threadstore.set('misago_admin_roles_choices', roles_choices)
+    ranks_choices = [('', _("All ranks"))]
+    for rank in Rank.objects.order_by('name').iterator():
+        ranks_choices.append((rank.pk, rank.name))
+
+    roles_choices = [('', _("All roles"))]
+    for role in Role.objects.order_by('name').iterator():
+        roles_choices.append((role.pk, role.name))
 
 
     extra_fields = {
     extra_fields = {
         'rank': forms.TypedChoiceField(
         'rank': forms.TypedChoiceField(
@@ -384,8 +376,7 @@ def SearchUsersForm(*args, **kwargs):
         )
         )
     }
     }
 
 
-    FinalForm = type('SearchUsersFormFinal', (SearchUsersFormBase, ), extra_fields)
-    return FinalForm(*args, **kwargs)
+    return type('SearchUsersForm', (BaseSearchUsersForm, ), extra_fields)
 
 
 
 
 class RankForm(forms.ModelForm):
 class RankForm(forms.ModelForm):

+ 0 - 11
misago/users/tests/test_activepostersranking.py

@@ -4,8 +4,6 @@ from django.contrib.auth import get_user_model
 from django.utils import timezone
 from django.utils import timezone
 
 
 from misago.categories.models import Category
 from misago.categories.models import Category
-from misago.core import threadstore
-from misago.core.cache import cache
 from misago.threads.testutils import post_thread
 from misago.threads.testutils import post_thread
 from misago.users.activepostersranking import (
 from misago.users.activepostersranking import (
     build_active_posters_ranking, get_active_posters_ranking)
     build_active_posters_ranking, get_active_posters_ranking)
@@ -19,17 +17,8 @@ class TestActivePostersRanking(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
         super().setUp()
         super().setUp()
 
 
-        cache.clear()
-        threadstore.clear()
-
         self.category = Category.objects.get(slug='first-category')
         self.category = Category.objects.get(slug='first-category')
 
 
-    def tearDown(self):
-        super().tearDown()
-
-        cache.clear()
-        threadstore.clear()
-
     def test_get_active_posters_ranking(self):
     def test_get_active_posters_ranking(self):
         """get_active_posters_ranking returns list of active posters"""
         """get_active_posters_ranking returns list of active posters"""
         # no posts, empty tanking
         # no posts, empty tanking

+ 19 - 24
misago/users/tests/test_users_api.py

@@ -8,16 +8,13 @@ from django.utils.encoding import smart_str
 
 
 from misago.acl.test import patch_user_acl
 from misago.acl.test import patch_user_acl
 from misago.categories.models import Category
 from misago.categories.models import Category
-from misago.core import threadstore
-from misago.core.cache import cache
 from misago.threads.models import Post, Thread
 from misago.threads.models import Post, Thread
 from misago.threads.testutils import post_thread
 from misago.threads.testutils import post_thread
 from misago.users.activepostersranking import build_active_posters_ranking
 from misago.users.activepostersranking import build_active_posters_ranking
 from misago.users.models import Ban, Rank
 from misago.users.models import Ban, Rank
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
-
-UserModel = get_user_model()
+User = get_user_model()
 
 
 
 
 class ActivePostersListTests(AuthenticatedUserTestCase):
 class ActivePostersListTests(AuthenticatedUserTestCase):
@@ -25,10 +22,8 @@ class ActivePostersListTests(AuthenticatedUserTestCase):
 
 
     def setUp(self):
     def setUp(self):
         super().setUp()
         super().setUp()
-        self.link = '/api/users/?list=active'
 
 
-        cache.clear()
-        threadstore.clear()
+        self.link = '/api/users/?list=active'
 
 
         self.category = Category.objects.all_categories()[:1][0]
         self.category = Category.objects.all_categories()[:1][0]
         self.category.labels = []
         self.category.labels = []
@@ -86,7 +81,7 @@ class FollowersListTests(AuthenticatedUserTestCase):
 
 
     def test_filled_list(self):
     def test_filled_list(self):
         """user with followers returns 200"""
         """user with followers returns 200"""
-        test_follower = UserModel.objects.create_user(
+        test_follower = User.objects.create_user(
             "TestFollower",
             "TestFollower",
             "test@follower.com",
             "test@follower.com",
             self.USER_PASSWORD,
             self.USER_PASSWORD,
@@ -99,7 +94,7 @@ class FollowersListTests(AuthenticatedUserTestCase):
 
 
     def test_filled_list_search(self):
     def test_filled_list_search(self):
         """followers list is searchable"""
         """followers list is searchable"""
-        test_follower = UserModel.objects.create_user(
+        test_follower = User.objects.create_user(
             "TestFollower",
             "TestFollower",
             "test@follower.com",
             "test@follower.com",
             self.USER_PASSWORD,
             self.USER_PASSWORD,
@@ -132,7 +127,7 @@ class FollowsListTests(AuthenticatedUserTestCase):
 
 
     def test_filled_list(self):
     def test_filled_list(self):
         """user with follows returns 200"""
         """user with follows returns 200"""
-        test_follower = UserModel.objects.create_user(
+        test_follower = User.objects.create_user(
             "TestFollower",
             "TestFollower",
             "test@follower.com",
             "test@follower.com",
             self.USER_PASSWORD,
             self.USER_PASSWORD,
@@ -145,7 +140,7 @@ class FollowsListTests(AuthenticatedUserTestCase):
 
 
     def test_filled_list_search(self):
     def test_filled_list_search(self):
         """follows list is searchable"""
         """follows list is searchable"""
-        test_follower = UserModel.objects.create_user(
+        test_follower = User.objects.create_user(
             "TestFollower",
             "TestFollower",
             "test@follower.com",
             "test@follower.com",
             self.USER_PASSWORD,
             self.USER_PASSWORD,
@@ -214,7 +209,7 @@ class RankListTests(AuthenticatedUserTestCase):
             is_tab=True,
             is_tab=True,
         )
         )
 
 
-        test_user = UserModel.objects.create_user(
+        test_user = User.objects.create_user(
             'Visible',
             'Visible',
             'visible@te.com',
             'visible@te.com',
             'Pass.123',
             'Pass.123',
@@ -255,7 +250,7 @@ class UserRetrieveTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
         super().setUp()
         super().setUp()
 
 
-        self.test_user = UserModel.objects.create_user('Tyrael', 't123@test.com', 'pass123')
+        self.test_user = User.objects.create_user('Tyrael', 't123@test.com', 'pass123')
         self.link = reverse(
         self.link = reverse(
             'misago:api:user-detail', kwargs={
             'misago:api:user-detail', kwargs={
                 'pk': self.test_user.pk,
                 'pk': self.test_user.pk,
@@ -399,7 +394,7 @@ class UserFollowTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
         super().setUp()
         super().setUp()
 
 
-        self.other_user = UserModel.objects.create_user("OtherUser", "other@user.com", "pass123")
+        self.other_user = User.objects.create_user("OtherUser", "other@user.com", "pass123")
 
 
         self.link = '/api/users/%s/follow/' % self.other_user.pk
         self.link = '/api/users/%s/follow/' % self.other_user.pk
 
 
@@ -435,13 +430,13 @@ class UserFollowTests(AuthenticatedUserTestCase):
         response = self.client.post(self.link)
         response = self.client.post(self.link)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
 
 
-        user = UserModel.objects.get(pk=self.user.pk)
+        user = User.objects.get(pk=self.user.pk)
         self.assertEqual(user.followers, 0)
         self.assertEqual(user.followers, 0)
         self.assertEqual(user.following, 1)
         self.assertEqual(user.following, 1)
         self.assertEqual(user.follows.count(), 1)
         self.assertEqual(user.follows.count(), 1)
         self.assertEqual(user.followed_by.count(), 0)
         self.assertEqual(user.followed_by.count(), 0)
 
 
-        followed = UserModel.objects.get(pk=self.other_user.pk)
+        followed = User.objects.get(pk=self.other_user.pk)
         self.assertEqual(followed.followers, 1)
         self.assertEqual(followed.followers, 1)
         self.assertEqual(followed.following, 0)
         self.assertEqual(followed.following, 0)
         self.assertEqual(followed.follows.count(), 0)
         self.assertEqual(followed.follows.count(), 0)
@@ -450,13 +445,13 @@ class UserFollowTests(AuthenticatedUserTestCase):
         response = self.client.post(self.link)
         response = self.client.post(self.link)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
 
 
-        user = UserModel.objects.get(pk=self.user.pk)
+        user = User.objects.get(pk=self.user.pk)
         self.assertEqual(user.followers, 0)
         self.assertEqual(user.followers, 0)
         self.assertEqual(user.following, 0)
         self.assertEqual(user.following, 0)
         self.assertEqual(user.follows.count(), 0)
         self.assertEqual(user.follows.count(), 0)
         self.assertEqual(user.followed_by.count(), 0)
         self.assertEqual(user.followed_by.count(), 0)
 
 
-        followed = UserModel.objects.get(pk=self.other_user.pk)
+        followed = User.objects.get(pk=self.other_user.pk)
         self.assertEqual(followed.followers, 0)
         self.assertEqual(followed.followers, 0)
         self.assertEqual(followed.following, 0)
         self.assertEqual(followed.following, 0)
         self.assertEqual(followed.follows.count(), 0)
         self.assertEqual(followed.follows.count(), 0)
@@ -469,7 +464,7 @@ class UserBanTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
         super().setUp()
         super().setUp()
 
 
-        self.other_user = UserModel.objects.create_user("OtherUser", "other@user.com", "pass123")
+        self.other_user = User.objects.create_user("OtherUser", "other@user.com", "pass123")
 
 
         self.link = '/api/users/%s/ban/' % self.other_user.pk
         self.link = '/api/users/%s/ban/' % self.other_user.pk
 
 
@@ -584,7 +579,7 @@ class UserDeleteTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
         super().setUp()
         super().setUp()
 
 
-        self.other_user = UserModel.objects.create_user("OtherUser", "other@user.com", "pass123")
+        self.other_user = User.objects.create_user("OtherUser", "other@user.com", "pass123")
 
 
         self.link = '/api/users/%s/delete/' % self.other_user.pk
         self.link = '/api/users/%s/delete/' % self.other_user.pk
 
 
@@ -698,8 +693,8 @@ class UserDeleteTests(AuthenticatedUserTestCase):
         )
         )
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
 
 
-        with self.assertRaises(UserModel.DoesNotExist):
-            UserModel.objects.get(pk=self.other_user.pk)
+        with self.assertRaises(User.DoesNotExist):
+            User.objects.get(pk=self.other_user.pk)
 
 
         self.assertEqual(Thread.objects.count(), self.threads)
         self.assertEqual(Thread.objects.count(), self.threads)
         self.assertEqual(Post.objects.count(), self.posts)
         self.assertEqual(Post.objects.count(), self.posts)
@@ -719,8 +714,8 @@ class UserDeleteTests(AuthenticatedUserTestCase):
         )
         )
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
 
 
-        with self.assertRaises(UserModel.DoesNotExist):
-            UserModel.objects.get(pk=self.other_user.pk)
+        with self.assertRaises(User.DoesNotExist):
+            User.objects.get(pk=self.other_user.pk)
 
 
         self.assertEqual(Thread.objects.count(), self.threads + 1)
         self.assertEqual(Thread.objects.count(), self.threads + 1)
         self.assertEqual(Post.objects.count(), self.posts + 2)
         self.assertEqual(Post.objects.count(), self.posts + 2)

+ 2 - 3
misago/users/testutils.py

@@ -1,6 +1,5 @@
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
-
-from misago.core.testutils import MisagoTestCase
+from django.test import TestCase
 
 
 from .models import AnonymousUser, Online
 from .models import AnonymousUser, Online
 from .setupnewuser import setup_new_user
 from .setupnewuser import setup_new_user
@@ -8,7 +7,7 @@ from .setupnewuser import setup_new_user
 User = get_user_model()
 User = get_user_model()
 
 
 
 
-class UserTestCase(MisagoTestCase):
+class UserTestCase(TestCase):
     USER_PASSWORD = "Pass.123"
     USER_PASSWORD = "Pass.123"
     USER_IP = '127.0.0.1'
     USER_IP = '127.0.0.1'
 
 

+ 7 - 3
misago/users/views/admin/users.py

@@ -13,9 +13,13 @@ from misago.core.mail import mail_users
 from misago.core.pgutils import chunk_queryset
 from misago.core.pgutils import chunk_queryset
 from misago.threads.models import Thread
 from misago.threads.models import Thread
 from misago.users.avatars.dynamic import set_avatar as set_dynamic_avatar
 from misago.users.avatars.dynamic import set_avatar as set_dynamic_avatar
-from misago.users.datadownloads import request_user_data_download, user_has_data_download_request
+from misago.users.datadownloads import (
+    request_user_data_download, user_has_data_download_request
+)
 from misago.users.forms.admin import (
 from misago.users.forms.admin import (
-    BanUsersForm, EditUserForm, EditUserFormFactory, NewUserForm, SearchUsersForm)
+    BanUsersForm, EditUserForm, EditUserFormFactory, NewUserForm,
+    create_search_users_form
+)
 from misago.users.models import Ban
 from misago.users.models import Ban
 from misago.users.profilefields import profilefields
 from misago.users.profilefields import profilefields
 from misago.users.setupnewuser import setup_new_user
 from misago.users.setupnewuser import setup_new_user
@@ -101,7 +105,7 @@ class UsersList(UserAdmin, generic.ListView):
         return qs.select_related('rank')
         return qs.select_related('rank')
 
 
     def get_search_form(self, request):
     def get_search_form(self, request):
-        return SearchUsersForm
+        return create_search_users_form()
 
 
     def action_activate(self, request, users):
     def action_activate(self, request, users):
         inactive_users = []
         inactive_users = []