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

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.admin.middleware.AdminAuthMiddleware',
     'misago.threads.middleware.UnreadThreadsCountMiddleware',
-    'misago.core.middleware.ThreadStoreMiddleware',
 ]
 
 ROOT_URLCONF = 'devproject.urls'

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

@@ -1,11 +1,6 @@
 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):
     """Superseded by 0004"""
 
@@ -14,7 +9,4 @@ class Migration(migrations.Migration):
         ('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
                 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)
             if request.GET.get('clear_filters'):
                 # Clear filters from querystring
                 request.session.pop(self.filters_session_key, None)
                 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
                     filtering_methods['GET'] != filtering_methods['session']):
@@ -181,12 +181,23 @@ class ListView(AdminView):
     def filters_session_key(self):
         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.is_valid()
         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, {})
         form = search_form(session_filters)
         form.is_valid()
@@ -198,18 +209,6 @@ class ListView(AdminView):
                 del data[key]
         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):
         for method in ('GET', 'session'):
             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.models import Category
-from misago.core.testutils import MisagoTestCase
 from misago.threads import testutils
 from misago.threads.threadtypes import trees_map
 
 
-class CategoryManagerTests(MisagoTestCase):
+class CategoryManagerTests(TestCase):
     def test_private_threads(self):
         """private_threads returns private threads category"""
         category = Category.objects.private_threads()
@@ -45,7 +46,7 @@ class CategoryManagerTests(MisagoTestCase):
                 self.assertNotIn(category.id, test_dict)
 
 
-class CategoryModelTests(MisagoTestCase):
+class CategoryModelTests(TestCase):
     def setUp(self):
         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.utils import get_categories_tree, get_category_path
 from misago.conftest import get_cache_versions
-from misago.core import threadstore
 from misago.users.testutils import AuthenticatedUserTestCase
 
 cache_versions = get_cache_versions()
@@ -33,9 +32,7 @@ class CategoriesUtilsTests(AuthenticatedUserTestCase):
         Category E
           + Subcategory F
         """
-
         super().setUp()
-        threadstore.clear()
 
         self.root = Category.objects.root_category()
         self.first_category = Category.objects.get(slug='first-category')

+ 0 - 4
misago/conf/dynamicsettings.py

@@ -45,10 +45,6 @@ class DynamicSettings:
         cls._overrides = {}
 
 
-def get_cache_name(cache_versions):
-    return "%s_%s" % (SETTINGS_CACHE, cache_versions[SETTINGS_CACHE])
-
-
 def get_settings_from_db():
     settings = {}
     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 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.dynamicsettings import DynamicSettings
 
 
 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()
         mock_request = Mock(settings=DynamicSettings(cache_versions))
         context_settings = conf(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('/')
         self.assertEqual(response.status_code, 200)
         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.models import SettingsGroup
-from misago.core import threadstore
 
 
 class DBConfMigrationUtilsTests(TestCase):
@@ -36,9 +35,6 @@ class DBConfMigrationUtilsTests(TestCase):
         migrationutils.migrate_settings_group(apps, self.test_group)
         self.groups_count = SettingsGroup.objects.count()
 
-    def tearDown(self):
-        threadstore.clear()
-
     def test_get_custom_group_and_settings(self):
         """tests setup created settings 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 misago.core import exceptionhandler, threadstore
+from misago.core import exceptionhandler
 from misago.core.utils import is_request_to_misago
 
 
@@ -19,9 +19,3 @@ class FrontendContextMiddleware(MiddlewareMixin):
     def process_request(self, request):
         request.include_frontend_context = True
         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.clear_state()
-
         Category.objects.partial_rebuild(self.root.tree_id)
 
         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.core.testutils import MisagoTestCase
 from misago.threads import testutils
 from misago.threads.utils import add_categories_to_items, get_thread_id_from_url
 
 
-class AddCategoriesToItemsTests(MisagoTestCase):
+class AddCategoriesToItemsTests(TestCase):
     def setUp(self):
         """
         Create categories tree for test cases:
@@ -19,7 +20,6 @@ class AddCategoriesToItemsTests(MisagoTestCase):
         Category E
           + Subcategory F
         """
-
         super().setUp()
 
         self.root = Category.objects.root_category()
@@ -90,8 +90,6 @@ class AddCategoriesToItemsTests(MisagoTestCase):
             save=True,
         )
 
-        self.clear_state()
-
         Category.objects.partial_rebuild(self.root.tree_id)
 
         self.root = Category.objects.root_category()
@@ -176,7 +174,7 @@ class MockRequest(object):
         return self.scheme == 'https'
 
 
-class GetThreadIdFromUrlTests(MisagoTestCase):
+class GetThreadIdFromUrlTests(TestCase):
     def test_get_thread_id_from_valid_urls(self):
         """get_thread_id_from_url extracts thread pk from valid urls"""
         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.admin.forms import IsoDateTimeField, YesNoSwitch
-from misago.conf import settings
-from misago.core import threadstore
 from misago.core.validators import validate_sluggable
 
 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
 
 
-class SearchUsersFormBase(forms.Form):
+class BaseSearchUsersForm(forms.Form):
     username = forms.CharField(label=_("Username starts with"), required=False)
     email = forms.CharField(label=_("E-mail starts with"), required=False)
     profilefields = forms.CharField(label=_("Profile fields contain"), required=False)
@@ -349,25 +347,19 @@ class SearchUsersFormBase(forms.Form):
         return queryset
 
 
-def SearchUsersForm(*args, **kwargs):
+def create_search_users_form():
     """
     Factory that uses cache for ranks and roles,
     and makes those ranks and roles typed choice fields that play nice
     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 = {
         '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):

+ 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 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.users.activepostersranking import (
     build_active_posters_ranking, get_active_posters_ranking)
@@ -19,17 +17,8 @@ class TestActivePostersRanking(AuthenticatedUserTestCase):
     def setUp(self):
         super().setUp()
 
-        cache.clear()
-        threadstore.clear()
-
         self.category = Category.objects.get(slug='first-category')
 
-    def tearDown(self):
-        super().tearDown()
-
-        cache.clear()
-        threadstore.clear()
-
     def test_get_active_posters_ranking(self):
         """get_active_posters_ranking returns list of active posters"""
         # 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.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.testutils import post_thread
 from misago.users.activepostersranking import build_active_posters_ranking
 from misago.users.models import Ban, Rank
 from misago.users.testutils import AuthenticatedUserTestCase
 
-
-UserModel = get_user_model()
+User = get_user_model()
 
 
 class ActivePostersListTests(AuthenticatedUserTestCase):
@@ -25,10 +22,8 @@ class ActivePostersListTests(AuthenticatedUserTestCase):
 
     def setUp(self):
         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.labels = []
@@ -86,7 +81,7 @@ class FollowersListTests(AuthenticatedUserTestCase):
 
     def test_filled_list(self):
         """user with followers returns 200"""
-        test_follower = UserModel.objects.create_user(
+        test_follower = User.objects.create_user(
             "TestFollower",
             "test@follower.com",
             self.USER_PASSWORD,
@@ -99,7 +94,7 @@ class FollowersListTests(AuthenticatedUserTestCase):
 
     def test_filled_list_search(self):
         """followers list is searchable"""
-        test_follower = UserModel.objects.create_user(
+        test_follower = User.objects.create_user(
             "TestFollower",
             "test@follower.com",
             self.USER_PASSWORD,
@@ -132,7 +127,7 @@ class FollowsListTests(AuthenticatedUserTestCase):
 
     def test_filled_list(self):
         """user with follows returns 200"""
-        test_follower = UserModel.objects.create_user(
+        test_follower = User.objects.create_user(
             "TestFollower",
             "test@follower.com",
             self.USER_PASSWORD,
@@ -145,7 +140,7 @@ class FollowsListTests(AuthenticatedUserTestCase):
 
     def test_filled_list_search(self):
         """follows list is searchable"""
-        test_follower = UserModel.objects.create_user(
+        test_follower = User.objects.create_user(
             "TestFollower",
             "test@follower.com",
             self.USER_PASSWORD,
@@ -214,7 +209,7 @@ class RankListTests(AuthenticatedUserTestCase):
             is_tab=True,
         )
 
-        test_user = UserModel.objects.create_user(
+        test_user = User.objects.create_user(
             'Visible',
             'visible@te.com',
             'Pass.123',
@@ -255,7 +250,7 @@ class UserRetrieveTests(AuthenticatedUserTestCase):
     def setUp(self):
         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(
             'misago:api:user-detail', kwargs={
                 'pk': self.test_user.pk,
@@ -399,7 +394,7 @@ class UserFollowTests(AuthenticatedUserTestCase):
     def setUp(self):
         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
 
@@ -435,13 +430,13 @@ class UserFollowTests(AuthenticatedUserTestCase):
         response = self.client.post(self.link)
         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.following, 1)
         self.assertEqual(user.follows.count(), 1)
         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.following, 0)
         self.assertEqual(followed.follows.count(), 0)
@@ -450,13 +445,13 @@ class UserFollowTests(AuthenticatedUserTestCase):
         response = self.client.post(self.link)
         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.following, 0)
         self.assertEqual(user.follows.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.following, 0)
         self.assertEqual(followed.follows.count(), 0)
@@ -469,7 +464,7 @@ class UserBanTests(AuthenticatedUserTestCase):
     def setUp(self):
         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
 
@@ -584,7 +579,7 @@ class UserDeleteTests(AuthenticatedUserTestCase):
     def setUp(self):
         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
 
@@ -698,8 +693,8 @@ class UserDeleteTests(AuthenticatedUserTestCase):
         )
         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(Post.objects.count(), self.posts)
@@ -719,8 +714,8 @@ class UserDeleteTests(AuthenticatedUserTestCase):
         )
         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(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 misago.core.testutils import MisagoTestCase
+from django.test import TestCase
 
 from .models import AnonymousUser, Online
 from .setupnewuser import setup_new_user
@@ -8,7 +7,7 @@ from .setupnewuser import setup_new_user
 User = get_user_model()
 
 
-class UserTestCase(MisagoTestCase):
+class UserTestCase(TestCase):
     USER_PASSWORD = "Pass.123"
     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.threads.models import Thread
 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 (
-    BanUsersForm, EditUserForm, EditUserFormFactory, NewUserForm, SearchUsersForm)
+    BanUsersForm, EditUserForm, EditUserFormFactory, NewUserForm,
+    create_search_users_form
+)
 from misago.users.models import Ban
 from misago.users.profilefields import profilefields
 from misago.users.setupnewuser import setup_new_user
@@ -101,7 +105,7 @@ class UsersList(UserAdmin, generic.ListView):
         return qs.select_related('rank')
 
     def get_search_form(self, request):
-        return SearchUsersForm
+        return create_search_users_form()
 
     def action_activate(self, request, users):
         inactive_users = []