Browse Source

#644: cut down number of serializers and dried up fields lists on those

Rafał Pitoń 8 years ago
parent
commit
07b2696a75

+ 0 - 1
frontend/src/utils/test-utils.js

@@ -38,7 +38,6 @@ export function mockUser(overrides) {
     },
     },
     avatar_hash: "5c6a04b4",
     avatar_hash: "5c6a04b4",
     email: "test@example.com",
     email: "test@example.com",
-    full_title: "Forum team",
     is_hiding_presence: false,
     is_hiding_presence: false,
     joined_on: "2015-05-09T16:13:33.973603Z",
     joined_on: "2015-05-09T16:13:33.973603Z",
     limits_private_thread_invites_to: 0,
     limits_private_thread_invites_to: 0,

+ 24 - 5
misago/core/serializers.py

@@ -7,7 +7,7 @@ class Subsettable(object):
         class Meta(cls.Meta):
         class Meta(cls.Meta):
             pass
             pass
 
 
-        Meta.fields = fields
+        Meta.fields = tuple(fields)
 
 
         return type(name, (cls,), {
         return type(name, (cls,), {
             'Meta': Meta
             'Meta': Meta
@@ -15,18 +15,37 @@ class Subsettable(object):
 
 
     @classmethod
     @classmethod
     def subset_exclude(cls, *fields):
     def subset_exclude(cls, *fields):
-        clean_fields = []
+        final_fields = []
         for field in cls.Meta.fields:
         for field in cls.Meta.fields:
             if field not in fields:
             if field not in fields:
-                clean_fields.append(field)
+                final_fields.append(field)
 
 
-        fields_in_name = [f.title().replace('_', '') for f in clean_fields]
+        fields_in_name = [f.title().replace('_', '') for f in final_fields]
         name = '{}{}Subset'.format(cls.__name__, ''.join(fields_in_name)[:100])
         name = '{}{}Subset'.format(cls.__name__, ''.join(fields_in_name)[:100])
 
 
         class Meta(cls.Meta):
         class Meta(cls.Meta):
             pass
             pass
 
 
-        Meta.fields = tuple(clean_fields)
+        Meta.fields = tuple(final_fields)
+
+        return type(name, (cls,), {
+            'Meta': Meta
+        })
+
+    @classmethod
+    def subset_extend(cls, *fields):
+        final_fields = list(cls.Meta.fields)
+        for field in fields:
+            if field not in final_fields:
+                final_fields.append(field)
+
+        fields_in_name = [f.title().replace('_', '') for f in final_fields]
+        name = '{}{}Subset'.format(cls.__name__, ''.join(fields_in_name)[:100])
+
+        class Meta(cls.Meta):
+            pass
+
+        Meta.fields = tuple(final_fields)
 
 
         return type(name, (cls,), {
         return type(name, (cls,), {
             'Meta': Meta
             'Meta': Meta

+ 9 - 30
misago/core/tests/test_serializers.py

@@ -54,27 +54,17 @@ class SubsettableSerializerTests(TestCase):
 
 
         self.assertFalse(TestSerializer.Meta.fields == serializer.Meta.fields)
         self.assertFalse(TestSerializer.Meta.fields == serializer.Meta.fields)
 
 
+    def test_create_subset_serializer_extend(self):
+        """classmethod extend creates new serializer"""
+        category = Category.objects.get(slug='first-category')
+        thread = testutils.post_thread(category=category)
 
 
-class TestRelatedSerializer(serializers.ModelSerializer):
-    class Meta:
-        model = Category
-        fields = (
-            'id',
-            'title',
-            'replies',
-            'has_unapproved_posts',
-            'started_on',
-            'last_post_on',
-            'last_post_is_event',
-            'last_post',
-            'last_poster_name',
-            'is_unapproved',
-            'is_hidden',
-            'is_closed',
-            'weight',
+        added_fields = ('category',)
 
 
-            'url',
-        )
+        serializer = TestSerializer.subset_extend(*added_fields)
+
+        serialized_thread = serializer(thread).data
+        self.assertEqual(serialized_thread['category'], category.pk)
 
 
 
 
 class TestSerializer(serializers.ModelSerializer, Subsettable):
 class TestSerializer(serializers.ModelSerializer, Subsettable):
@@ -96,15 +86,4 @@ class TestSerializer(serializers.ModelSerializer, Subsettable):
             'is_hidden',
             'is_hidden',
             'is_closed',
             'is_closed',
             'weight',
             'weight',
-
-            'url',
         )
         )
-
-    def get_url(self, obj):
-        return {
-            'index': obj.get_absolute_url(),
-            'new_post': obj.get_new_post_url(),
-            'last_post': obj.get_last_post_url(),
-            'unapproved_post': obj.get_unapproved_post_url(),
-            'last_poster': self.get_last_poster_url(obj),
-        }

+ 10 - 5
misago/threads/serializers/feed.py

@@ -5,7 +5,7 @@ from django.urls import reverse
 from misago.categories.serializers import CategorySerializer
 from misago.categories.serializers import CategorySerializer
 from misago.core.serializers import Subsettable
 from misago.core.serializers import Subsettable
 from misago.threads.models import Post
 from misago.threads.models import Post
-from misago.users.serializers import BasicUserSerializer
+from misago.users.serializers import UserSerializer
 
 
 from .post import PostSerializer
 from .post import PostSerializer
 
 
@@ -15,13 +15,18 @@ __all__ = [
 ]
 ]
 
 
 
 
-CategoryFeedSerializer = CategorySerializer.subset(
+
+FeedUserSerializer = UserSerializer.subset(
+    'id', 'username', 'avatars', 'absolute_url')
+
+
+FeedCategorySerializer = CategorySerializer.subset(
     'name', 'css_class', 'absolute_url')
     'name', 'css_class', 'absolute_url')
 
 
 
 
 class FeedSerializer(PostSerializer, Subsettable):
 class FeedSerializer(PostSerializer, Subsettable):
-    poster = BasicUserSerializer(many=False, read_only=True)
-    category = CategoryFeedSerializer(many=False, read_only=True)
+    poster = FeedUserSerializer(many=False, read_only=True)
+    category = FeedCategorySerializer(many=False, read_only=True)
 
 
     thread = serializers.SerializerMethodField()
     thread = serializers.SerializerMethodField()
     top_category = serializers.SerializerMethodField()
     top_category = serializers.SerializerMethodField()
@@ -43,6 +48,6 @@ class FeedSerializer(PostSerializer, Subsettable):
 
 
     def get_top_category(self, obj):
     def get_top_category(self, obj):
         try:
         try:
-            return CategoryFeedSerializer(obj.top_category).data
+            return FeedCategorySerializer(obj.top_category).data
         except AttributeError:
         except AttributeError:
             return None
             return None

+ 8 - 5
misago/threads/serializers/post.py

@@ -3,16 +3,19 @@ from rest_framework import serializers
 from django.urls import reverse
 from django.urls import reverse
 
 
 from misago.categories.serializers import CategorySerializer
 from misago.categories.serializers import CategorySerializer
+from misago.core.serializers import Subsettable
 from misago.threads.models import Post
 from misago.threads.models import Post
-from misago.users.serializers import BasicUserSerializer, UserSerializer
+from misago.users.serializers import UserSerializer as BaseUserSerializer
 
 
 
 
-__all__ = [
-    'PostSerializer',
-]
+__all__ = ['PostSerializer']
 
 
 
 
-class PostSerializer(serializers.ModelSerializer):
+UserSerializer = BaseUserSerializer.subset(
+    'id', 'username', 'rank', 'avatars', 'signature', 'short_title', 'status', 'absolute_url')
+
+
+class PostSerializer(serializers.ModelSerializer, Subsettable):
     poster = UserSerializer(many=False, read_only=True)
     poster = UserSerializer(many=False, read_only=True)
     poster_ip = serializers.SerializerMethodField()
     poster_ip = serializers.SerializerMethodField()
     content = serializers.SerializerMethodField()
     content = serializers.SerializerMethodField()

+ 5 - 2
misago/users/api/userendpoints/list.py

@@ -13,7 +13,7 @@ from misago.core.shortcuts import get_int_or_404, get_object_or_404, paginate, p
 from misago.users.activepostersranking import get_active_posters_ranking
 from misago.users.activepostersranking import get_active_posters_ranking
 from misago.users.models import Rank
 from misago.users.models import Rank
 from misago.users.online.utils import make_users_status_aware
 from misago.users.online.utils import make_users_status_aware
-from misago.users.serializers import ScoredUserSerializer, UserSerializer
+from misago.users.serializers import UserCardSerializer
 
 
 
 
 UserModel = get_user_model()
 UserModel = get_user_model()
@@ -69,7 +69,7 @@ def generic(request):
 
 
     make_users_status_aware(request.user, list_page.object_list)
     make_users_status_aware(request.user, list_page.object_list)
 
 
-    return paginated_response(list_page, serializer=UserSerializer)
+    return paginated_response(list_page, serializer=UserCardSerializer)
 
 
 
 
 LISTS = {
 LISTS = {
@@ -85,3 +85,6 @@ def list_endpoint(request):
         return list_handler(request)
         return list_handler(request)
     else:
     else:
         return generic(request)
         return generic(request)
+
+
+ScoredUserSerializer = UserCardSerializer.subset_extend('meta')

+ 8 - 1
misago/users/api/users.py

@@ -25,7 +25,7 @@ from misago.users.permissions.delete import allow_delete_user
 from misago.users.permissions.moderation import allow_moderate_avatar, allow_rename_user
 from misago.users.permissions.moderation import allow_moderate_avatar, allow_rename_user
 from misago.users.permissions.profiles import (
 from misago.users.permissions.profiles import (
     allow_browse_users_list, allow_follow_user, allow_see_ban_details)
     allow_browse_users_list, allow_follow_user, allow_see_ban_details)
-from misago.users.serializers import BanDetailsSerializer, UserProfileSerializer, UserSerializer
+from misago.users.serializers import BanDetailsSerializer, UserSerializer
 from misago.users.viewmodels import UserPosts, UserThreads
 from misago.users.viewmodels import UserPosts, UserThreads
 
 
 from .rest_permissions import BasePermission, UnbannedAnonOnly
 from .rest_permissions import BasePermission, UnbannedAnonOnly
@@ -256,3 +256,10 @@ class UserViewSet(viewsets.GenericViewSet):
         feed = UserPosts(request, profile, page)
         feed = UserPosts(request, profile, page)
 
 
         return Response(feed.get_frontend_context())
         return Response(feed.get_frontend_context())
+
+
+UserProfileSerializer = UserSerializer.subset(
+    'id', 'username', 'slug', 'email', 'joined_on', 'rank', 'title', 'avatars',
+    'is_avatar_locked', 'signature', 'is_signature_locked', 'followers', 'following',
+    'threads', 'posts', 'acl', 'is_followed', 'is_blocked', 'status', 'absolute_url',
+    'api_url')

+ 0 - 4
misago/users/models/user.py

@@ -305,10 +305,6 @@ class User(AbstractBaseUser, PermissionsMixin):
         raise TypeError('Cannot make User instances ACL aware')
         raise TypeError('Cannot make User instances ACL aware')
 
 
     @property
     @property
-    def full_title(self):
-        return self.title or self.rank.name
-
-    @property
     def short_title(self):
     def short_title(self):
         return self.title or self.rank.title or self.rank.name
         return self.title or self.rank.title or self.rank.name
 
 

+ 2 - 2
misago/users/search.py

@@ -5,7 +5,7 @@ from django.utils.translation import ugettext_lazy
 
 
 from misago.search import SearchProvider
 from misago.search import SearchProvider
 
 
-from .serializers import UserSerializer
+from .serializers import UserCardSerializer
 
 
 
 
 HEAD_RESULTS = 8
 HEAD_RESULTS = 8
@@ -33,7 +33,7 @@ class SearchUsers(SearchProvider):
             results = []
             results = []
 
 
         return {
         return {
-            'results': UserSerializer(results, many=True).data,
+            'results': UserCardSerializer(results, many=True).data,
             'count': len(results)
             'count': len(results)
         }
         }
 
 

+ 1 - 0
misago/users/serializers/__init__.py

@@ -1,3 +1,4 @@
 from .ban import *
 from .ban import *
 from .rank import *
 from .rank import *
 from .user import *
 from .user import *
+from .auth import *

+ 54 - 8
misago/users/serializers/auth.py

@@ -1,26 +1,72 @@
 from rest_framework import serializers
 from rest_framework import serializers
 
 
+from django.contrib.auth import get_user_model
+from django.urls import reverse
+
 from misago.acl import serialize_acl
 from misago.acl import serialize_acl
 
 
+from .user import UserSerializer
+
+
+UserModel = get_user_model()
+
+__all__ = ['AuthenticatedUserSerializer', 'AnonymousUserSerializer',]
 
 
-class AuthenticatedUserSerializer(serializers.ModelSerializer):
-    is_authenticated = serializers.SerializerMethodField()
 
 
+class AuthFlags(object):
     def get_is_authenticated(self, obj):
     def get_is_authenticated(self, obj):
-        return False
-    pass
+        return bool(obj.is_authenticated)
+
+    def get_is_anonymous(self, obj):
+        return bool(obj.is_anonymous)
+
+
+class AuthenticatedUserSerializer(UserSerializer, AuthFlags):
+    email = serializers.SerializerMethodField()
+    is_authenticated = serializers.SerializerMethodField()
+    is_anonymous = serializers.SerializerMethodField()
 
 
+    class Meta:
+        model = UserModel
+        fields = UserSerializer.Meta.fields + (
+            'is_authenticated',
+            'is_anonymous',
+        )
 
 
-class AnonymousUserSerializer(serializers.Serializer):
+    def get_acl(self, obj):
+        return serialize_acl(obj)
+
+    def get_email(self, obj):
+        return obj.email
+
+    def get_api_url(self, obj):
+        return {
+            'avatar': reverse(
+                'misago:api:user-avatar', kwargs={'pk': obj.pk}),
+            'options': reverse(
+                'misago:api:user-forum-options', kwargs={'pk': obj.pk}),
+            'username': reverse(
+                'misago:api:user-username', kwargs={'pk': obj.pk}),
+            'change_email': reverse(
+                'misago:api:user-change-email', kwargs={'pk': obj.pk}),
+            'change_password': reverse(
+                'misago:api:user-change-password', kwargs={'pk': obj.pk}),
+        }
+
+AuthenticatedUserSerializer = AuthenticatedUserSerializer.subset_exclude(
+    'is_avatar_locked', 'is_blocked', 'is_followed', 'is_signature_locked',
+    'meta', 'signature', 'status',
+)
+
+
+class AnonymousUserSerializer(serializers.Serializer, AuthFlags):
     id = serializers.ReadOnlyField()
     id = serializers.ReadOnlyField()
     acl = serializers.SerializerMethodField()
     acl = serializers.SerializerMethodField()
     is_authenticated = serializers.SerializerMethodField()
     is_authenticated = serializers.SerializerMethodField()
+    is_anonymous = serializers.SerializerMethodField()
 
 
     def get_acl(self, obj):
     def get_acl(self, obj):
         if hasattr(obj, 'acl'):
         if hasattr(obj, 'acl'):
             return serialize_acl(obj)
             return serialize_acl(obj)
         else:
         else:
             return {}
             return {}
-
-    def get_is_authenticated(self, obj):
-        return False

+ 34 - 10
misago/users/serializers/user.py

@@ -4,10 +4,16 @@ from django.contrib.auth import get_user_model
 from django.urls import reverse
 from django.urls import reverse
 
 
 from misago.acl import serialize_acl
 from misago.acl import serialize_acl
+from misago.core.serializers import Subsettable
 
 
 from . import RankSerializer
 from . import RankSerializer
 
 
 
 
+UserModel = get_user_model()
+
+__all__ = ['StatusSerializer', 'UserSerializer', 'UserCardSerializer']
+
+
 class StatusSerializer(serializers.Serializer):
 class StatusSerializer(serializers.Serializer):
     is_offline = serializers.BooleanField()
     is_offline = serializers.BooleanField()
     is_online = serializers.BooleanField()
     is_online = serializers.BooleanField()
@@ -20,19 +26,19 @@ class StatusSerializer(serializers.Serializer):
     banned_until = serializers.DateTimeField()
     banned_until = serializers.DateTimeField()
 
 
 
 
-class UserSerializer(serializers.ModelSerializer):
+class UserSerializer(serializers.ModelSerializer, Subsettable):
     email = serializers.SerializerMethodField()
     email = serializers.SerializerMethodField()
     rank = RankSerializer(many=False, read_only=True)
     rank = RankSerializer(many=False, read_only=True)
+    signature = serializers.SerializerMethodField()
+
+    acl = serializers.SerializerMethodField()
     is_followed = serializers.SerializerMethodField()
     is_followed = serializers.SerializerMethodField()
     is_blocked = serializers.SerializerMethodField()
     is_blocked = serializers.SerializerMethodField()
-    short_title = serializers.SerializerMethodField()
-    status = serializers.SerializerMethodField()
-    signature = serializers.SerializerMethodField()
     meta = serializers.SerializerMethodField()
     meta = serializers.SerializerMethodField()
-    acl = serializers.SerializerMethodField()
-    api_url = serializers.SerializerMethodField()
+    status = serializers.SerializerMethodField()
 
 
     absolute_url = serializers.SerializerMethodField()
     absolute_url = serializers.SerializerMethodField()
+    api_url = serializers.SerializerMethodField()
 
 
     class Meta:
     class Meta:
         model = UserModel
         model = UserModel
@@ -40,20 +46,33 @@ class UserSerializer(serializers.ModelSerializer):
             'id',
             'id',
             'username',
             'username',
             'slug',
             'slug',
+            'email',
             'joined_on',
             'joined_on',
-            'avatars',
+            'rank',
             'title',
             'title',
             'short_title',
             'short_title',
-            'rank',
+            'avatars',
+            'is_avatar_locked',
             'signature',
             'signature',
-            'threads',
-            'posts',
+            'is_signature_locked',
             'followers',
             'followers',
             'following',
             'following',
+            'threads',
+            'posts',
+
+            'acl',
+            'is_followed',
+            'is_blocked',
+            'meta',
             'status',
             'status',
+
             'absolute_url',
             'absolute_url',
+            'api_url',
         )
         )
 
 
+    def get_acl(self, obj):
+        return obj.acl_
+
     def get_email(self, obj):
     def get_email(self, obj):
         if (obj == self.context['user'] or
         if (obj == self.context['user'] or
                 self.context['user'].acl['can_see_users_emails']):
                 self.context['user'].acl['can_see_users_emails']):
@@ -107,3 +126,8 @@ class UserSerializer(serializers.ModelSerializer):
             'threads': reverse('misago:api:user-threads', kwargs={'pk': obj.pk}),
             'threads': reverse('misago:api:user-threads', kwargs={'pk': obj.pk}),
             'posts': reverse('misago:api:user-posts', kwargs={'pk': obj.pk}),
             'posts': reverse('misago:api:user-posts', kwargs={'pk': obj.pk}),
         }
         }
+
+
+UserCardSerializer = UserSerializer.subset(
+    'id', 'username', 'joined_on', 'rank', 'title', 'avatars', 'followers',
+    'threads', 'posts', 'status', 'absolute_url')

+ 7 - 3
misago/users/serializers/usernamechange.py

@@ -2,15 +2,19 @@ from rest_framework import serializers
 
 
 from misago.users.models import UsernameChange
 from misago.users.models import UsernameChange
 
 
-from .user import BasicUserSerializer
+from .user import UserSerializer as BaseUserSerializer
 
 
 
 
 __all__ = ['UsernameChangeSerializer']
 __all__ = ['UsernameChangeSerializer']
 
 
 
 
+UserSerializer = BaseUserSerializer.subset(
+    'id', 'username', 'avatars', 'absolute_url')
+
+
 class UsernameChangeSerializer(serializers.ModelSerializer):
 class UsernameChangeSerializer(serializers.ModelSerializer):
-    user = BasicUserSerializer(many=False, read_only=True)
-    changed_by = BasicUserSerializer(many=False, read_only=True)
+    user = UserSerializer(many=False, read_only=True)
+    changed_by = UserSerializer(many=False, read_only=True)
 
 
     class Meta:
     class Meta:
         model = UsernameChange
         model = UsernameChange

+ 5 - 2
misago/users/views/lists.py

@@ -11,7 +11,7 @@ from misago.users.activepostersranking import get_active_posters_ranking
 from misago.users.models import Rank
 from misago.users.models import Rank
 from misago.users.pages import users_list
 from misago.users.pages import users_list
 from misago.users.permissions.profiles import allow_browse_users_list
 from misago.users.permissions.profiles import allow_browse_users_list
-from misago.users.serializers import ScoredUserSerializer, UserSerializer
+from misago.users.serializers import UserCardSerializer
 
 
 
 
 def render(request, template, context):
 def render(request, template, context):
@@ -104,7 +104,7 @@ def rank(request, slug, page=0):
 
 
     data = pagination_dict(page)
     data = pagination_dict(page)
     data.update({
     data.update({
-        'results': UserSerializer(page.object_list, many=True).data
+        'results': UserCardSerializer(page.object_list, many=True).data
     })
     })
 
 
     request.frontend_context['USERS'] = data
     request.frontend_context['USERS'] = data
@@ -124,3 +124,6 @@ def rank(request, slug, page=0):
 
 
         'paginator': data
         'paginator': data
     })
     })
+
+
+ScoredUserSerializer = UserCardSerializer.subset_extend('meta')

+ 10 - 3
misago/users/views/profile.py

@@ -20,7 +20,7 @@ from misago.users.decorators import deny_guests
 from misago.users.online.utils import get_user_status
 from misago.users.online.utils import get_user_status
 from misago.users.pages import user_profile
 from misago.users.pages import user_profile
 from misago.users.permissions.profiles import allow_block_user, allow_follow_user
 from misago.users.permissions.profiles import allow_block_user, allow_follow_user
-from misago.users.serializers import BanDetailsSerializer, UserProfileSerializer, UserSerializer
+from misago.users.serializers import BanDetailsSerializer, UserSerializer, UserCardSerializer
 from misago.users.serializers.usernamechange import UsernameChangeSerializer
 from misago.users.serializers.usernamechange import UsernameChangeSerializer
 from misago.users.viewmodels import UserPosts, UserThreads
 from misago.users.viewmodels import UserPosts, UserThreads
 
 
@@ -142,7 +142,7 @@ def followers(request, profile):
     paginator = pagination_dict(page)
     paginator = pagination_dict(page)
 
 
     request.frontend_context['PROFILE_FOLLOWERS'] = dict(
     request.frontend_context['PROFILE_FOLLOWERS'] = dict(
-        results=UserSerializer(page.object_list, many=True).data,
+        results=UserCardSerializer(page.object_list, many=True).data,
         **paginator
         **paginator
     )
     )
 
 
@@ -161,7 +161,7 @@ def follows(request, profile):
     paginator = pagination_dict(page)
     paginator = pagination_dict(page)
 
 
     request.frontend_context['PROFILE_FOLLOWS'] = dict(
     request.frontend_context['PROFILE_FOLLOWS'] = dict(
-        results=UserSerializer(page.object_list, many=True).data,
+        results=UserCardSerializer(page.object_list, many=True).data,
         **paginator
         **paginator
     )
     )
 
 
@@ -203,3 +203,10 @@ def user_ban(request, profile):
         'profile': profile,
         'profile': profile,
         'ban': ban,
         'ban': ban,
     })
     })
+
+
+UserProfileSerializer = UserSerializer.subset(
+    'id', 'username', 'slug', 'email', 'joined_on', 'rank', 'title', 'avatars',
+    'is_avatar_locked', 'signature', 'is_signature_locked', 'followers', 'following',
+    'threads', 'posts', 'acl', 'is_followed', 'is_blocked', 'status', 'absolute_url',
+    'api_url')