Migrating users serializers and validators

remotes/origin/issue/4795/notification_even_they_are_disabled
Jesús Espino 2016-07-05 19:29:49 +02:00
parent 39075288ac
commit 7d2b6c34ce
5 changed files with 214 additions and 162 deletions

View File

@ -25,3 +25,7 @@ def dict_sum(*args):
assert isinstance(arg, dict) assert isinstance(arg, dict)
result += collections.Counter(arg) result += collections.Counter(arg)
return result return result
def into_namedtuple(dictionary):
return collections.namedtuple('GenericDict', dictionary.keys())(**dictionary)

View File

@ -19,7 +19,6 @@
import uuid import uuid
from django.apps import apps from django.apps import apps
from django.db.models import Q, F
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.core.validators import validate_email from django.core.validators import validate_email
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
@ -28,21 +27,21 @@ from django.conf import settings
from taiga.base import exceptions as exc from taiga.base import exceptions as exc
from taiga.base import filters from taiga.base import filters
from taiga.base import response from taiga.base import response
from taiga.base.utils.dicts import into_namedtuple
from taiga.auth.tokens import get_user_for_token from taiga.auth.tokens import get_user_for_token
from taiga.base.decorators import list_route from taiga.base.decorators import list_route
from taiga.base.decorators import detail_route from taiga.base.decorators import detail_route
from taiga.base.api import ModelCrudViewSet from taiga.base.api import ModelCrudViewSet
from taiga.base.api.mixins import BlockedByProjectMixin from taiga.base.api.mixins import BlockedByProjectMixin
from taiga.base.filters import PermissionBasedFilterBackend
from taiga.base.api.utils import get_object_or_404 from taiga.base.api.utils import get_object_or_404
from taiga.base.filters import MembersFilterBackend from taiga.base.filters import MembersFilterBackend
from taiga.base.mails import mail_builder from taiga.base.mails import mail_builder
from taiga.projects.votes import services as votes_service
from taiga.users.services import get_user_by_username_or_email from taiga.users.services import get_user_by_username_or_email
from easy_thumbnails.source_generators import pil_image from easy_thumbnails.source_generators import pil_image
from . import models from . import models
from . import serializers from . import serializers
from . import validators
from . import permissions from . import permissions
from . import filters as user_filters from . import filters as user_filters
from . import services from . import services
@ -53,6 +52,8 @@ class UsersViewSet(ModelCrudViewSet):
permission_classes = (permissions.UserPermission,) permission_classes = (permissions.UserPermission,)
admin_serializer_class = serializers.UserAdminSerializer admin_serializer_class = serializers.UserAdminSerializer
serializer_class = serializers.UserSerializer serializer_class = serializers.UserSerializer
admin_validator_class = validators.UserAdminValidator
validator_class = validators.UserValidator
queryset = models.User.objects.all().prefetch_related("memberships") queryset = models.User.objects.all().prefetch_related("memberships")
filter_backends = (MembersFilterBackend,) filter_backends = (MembersFilterBackend,)
@ -64,6 +65,14 @@ class UsersViewSet(ModelCrudViewSet):
return self.serializer_class return self.serializer_class
def get_validator_class(self):
if self.action in ["partial_update", "update", "retrieve", "by_username"]:
user = self.object
if self.request.user == user or self.request.user.is_superuser:
return self.admin_validator_class
return self.validator_class
def create(self, *args, **kwargs): def create(self, *args, **kwargs):
raise exc.NotSupported() raise exc.NotSupported()
@ -86,7 +95,7 @@ class UsersViewSet(ModelCrudViewSet):
serializer = self.get_serializer(self.object) serializer = self.get_serializer(self.object)
return response.Ok(serializer.data) return response.Ok(serializer.data)
#TODO: commit_on_success # TODO: commit_on_success
def partial_update(self, request, *args, **kwargs): def partial_update(self, request, *args, **kwargs):
""" """
We must detect if the user is trying to change his email so we can We must detect if the user is trying to change his email so we can
@ -96,12 +105,10 @@ class UsersViewSet(ModelCrudViewSet):
user = self.get_object() user = self.get_object()
self.check_permissions(request, "update", user) self.check_permissions(request, "update", user)
ret = super().partial_update(request, *args, **kwargs)
new_email = request.DATA.get('email', None) new_email = request.DATA.get('email', None)
if new_email is not None: if new_email is not None:
valid_new_email = True valid_new_email = True
duplicated_email = models.User.objects.filter(email = new_email).exists() duplicated_email = models.User.objects.filter(email=new_email).exists()
try: try:
validate_email(new_email) validate_email(new_email)
@ -115,14 +122,21 @@ class UsersViewSet(ModelCrudViewSet):
elif not valid_new_email: elif not valid_new_email:
raise exc.WrongArguments(_("Not valid email")) raise exc.WrongArguments(_("Not valid email"))
#We need to generate a token for the email # We need to generate a token for the email
request.user.email_token = str(uuid.uuid1()) request.user.email_token = str(uuid.uuid1())
request.user.new_email = new_email request.user.new_email = new_email
request.user.save(update_fields=["email_token", "new_email"]) request.user.save(update_fields=["email_token", "new_email"])
email = mail_builder.change_email(request.user.new_email, {"user": request.user, email = mail_builder.change_email(
"lang": request.user.lang}) request.user.new_email,
{
"user": request.user,
"lang": request.user.lang
}
)
email.send() email.send()
ret = super().partial_update(request, *args, **kwargs)
return ret return ret
def destroy(self, request, pk=None): def destroy(self, request, pk=None):
@ -165,16 +179,16 @@ class UsersViewSet(ModelCrudViewSet):
self.check_permissions(request, "change_password_from_recovery", None) self.check_permissions(request, "change_password_from_recovery", None)
serializer = serializers.RecoverySerializer(data=request.DATA, many=False) validator = validators.RecoveryValidator(data=request.DATA, many=False)
if not serializer.is_valid(): if not validator.is_valid():
raise exc.WrongArguments(_("Token is invalid")) raise exc.WrongArguments(_("Token is invalid"))
try: try:
user = models.User.objects.get(token=serializer.data["token"]) user = models.User.objects.get(token=validator.data["token"])
except models.User.DoesNotExist: except models.User.DoesNotExist:
raise exc.WrongArguments(_("Token is invalid")) raise exc.WrongArguments(_("Token is invalid"))
user.set_password(serializer.data["password"]) user.set_password(validator.data["password"])
user.token = None user.token = None
user.save(update_fields=["password", "token"]) user.save(update_fields=["password", "token"])
@ -247,13 +261,13 @@ class UsersViewSet(ModelCrudViewSet):
""" """
Verify the email change to current logged user. Verify the email change to current logged user.
""" """
serializer = serializers.ChangeEmailSerializer(data=request.DATA, many=False) validator = validators.ChangeEmailValidator(data=request.DATA, many=False)
if not serializer.is_valid(): if not validator.is_valid():
raise exc.WrongArguments(_("Invalid, are you sure the token is correct and you " raise exc.WrongArguments(_("Invalid, are you sure the token is correct and you "
"didn't use it before?")) "didn't use it before?"))
try: try:
user = models.User.objects.get(email_token=serializer.data["email_token"]) user = models.User.objects.get(email_token=validator.data["email_token"])
except models.User.DoesNotExist: except models.User.DoesNotExist:
raise exc.WrongArguments(_("Invalid, are you sure the token is correct and you " raise exc.WrongArguments(_("Invalid, are you sure the token is correct and you "
"didn't use it before?")) "didn't use it before?"))
@ -280,13 +294,13 @@ class UsersViewSet(ModelCrudViewSet):
""" """
Cancel an account via token Cancel an account via token
""" """
serializer = serializers.CancelAccountSerializer(data=request.DATA, many=False) validator = validators.CancelAccountValidator(data=request.DATA, many=False)
if not serializer.is_valid(): if not validator.is_valid():
raise exc.WrongArguments(_("Invalid, are you sure the token is correct?")) raise exc.WrongArguments(_("Invalid, are you sure the token is correct?"))
try: try:
max_age_cancel_account = getattr(settings, "MAX_AGE_CANCEL_ACCOUNT", None) max_age_cancel_account = getattr(settings, "MAX_AGE_CANCEL_ACCOUNT", None)
user = get_user_for_token(serializer.data["cancel_token"], "cancel_account", user = get_user_for_token(validator.data["cancel_token"], "cancel_account",
max_age=max_age_cancel_account) max_age=max_age_cancel_account)
except exc.NotAuthenticated: except exc.NotAuthenticated:
@ -305,7 +319,7 @@ class UsersViewSet(ModelCrudViewSet):
self.object_list = user_filters.ContactsFilterBackend().filter_queryset( self.object_list = user_filters.ContactsFilterBackend().filter_queryset(
user, request, self.get_queryset(), self).extra( user, request, self.get_queryset(), self).extra(
select={"complete_user_name":"concat(full_name, username)"}).order_by("complete_user_name") select={"complete_user_name": "concat(full_name, username)"}).order_by("complete_user_name")
page = self.paginate_queryset(self.object_list) page = self.paginate_queryset(self.object_list)
if page is not None: if page is not None:
@ -349,10 +363,10 @@ class UsersViewSet(ModelCrudViewSet):
for elem in elements: for elem in elements:
if elem["type"] == "project": if elem["type"] == "project":
# projects are liked objects # projects are liked objects
response_data.append(serializers.LikedObjectSerializer(elem, **extra_args_liked).data ) response_data.append(serializers.LikedObjectSerializer(into_namedtuple(elem), **extra_args_liked).data)
else: else:
# stories, tasks and issues are voted objects # stories, tasks and issues are voted objects
response_data.append(serializers.VotedObjectSerializer(elem, **extra_args_voted).data ) response_data.append(serializers.VotedObjectSerializer(into_namedtuple(elem), **extra_args_voted).data)
return response.Ok(response_data) return response.Ok(response_data)
@ -374,7 +388,7 @@ class UsersViewSet(ModelCrudViewSet):
"user_likes": services.get_liked_content_for_user(request.user), "user_likes": services.get_liked_content_for_user(request.user),
} }
response_data = [serializers.LikedObjectSerializer(elem, **extra_args).data for elem in elements] response_data = [serializers.LikedObjectSerializer(into_namedtuple(elem), **extra_args).data for elem in elements]
return response.Ok(response_data) return response.Ok(response_data)
@ -397,17 +411,18 @@ class UsersViewSet(ModelCrudViewSet):
"user_votes": services.get_voted_content_for_user(request.user), "user_votes": services.get_voted_content_for_user(request.user),
} }
response_data = [serializers.VotedObjectSerializer(elem, **extra_args).data for elem in elements] response_data = [serializers.VotedObjectSerializer(into_namedtuple(elem), **extra_args).data for elem in elements]
return response.Ok(response_data) return response.Ok(response_data)
######################################################
## Role
######################################################
######################################################
# Role
######################################################
class RolesViewSet(BlockedByProjectMixin, ModelCrudViewSet): class RolesViewSet(BlockedByProjectMixin, ModelCrudViewSet):
model = models.Role model = models.Role
serializer_class = serializers.RoleSerializer serializer_class = serializers.RoleSerializer
validator_class = validators.RoleValidator
permission_classes = (permissions.RolesPermission, ) permission_classes = (permissions.RolesPermission, )
filter_backends = (filters.CanViewProjectFilterBackend,) filter_backends = (filters.CanViewProjectFilterBackend,)
filter_fields = ('project',) filter_fields = ('project',)

View File

@ -22,7 +22,7 @@ from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from taiga.base.api import serializers from taiga.base.api import serializers
from taiga.base.fields import PgArrayField, Field, MethodField from taiga.base.fields import PgArrayField, Field, MethodField, I18NField
from taiga.base.utils.thumbnails import get_thumbnail_url from taiga.base.utils.thumbnails import get_thumbnail_url
@ -40,47 +40,28 @@ import re
# User # User
###################################################### ######################################################
class ContactProjectDetailSerializer(serializers.ModelSerializer): class ContactProjectDetailSerializer(serializers.LightSerializer):
class Meta: id = Field()
model = Project slug = Field()
fields = ("id", "slug", "name") name = Field()
class UserSerializer(serializers.ModelSerializer): class UserSerializer(serializers.LightSerializer):
full_name_display = serializers.SerializerMethodField("get_full_name_display") id = Field()
photo = serializers.SerializerMethodField("get_photo") username = Field()
big_photo = serializers.SerializerMethodField("get_big_photo") full_name = Field()
gravatar_url = serializers.SerializerMethodField("get_gravatar_url") full_name_display = MethodField()
roles = serializers.SerializerMethodField("get_roles") color = Field()
projects_with_me = serializers.SerializerMethodField("get_projects_with_me") bio = Field()
lang = Field()
class Meta: theme = Field()
model = User timezone = Field()
# IMPORTANT: Maintain the UserAdminSerializer Meta up to date is_active = Field()
# with this info (including there the email) photo = MethodField()
fields = ("id", "username", "full_name", "full_name_display", big_photo = MethodField()
"color", "bio", "lang", "theme", "timezone", "is_active", gravatar_url = MethodField()
"photo", "big_photo", "roles", "projects_with_me", roles = MethodField()
"gravatar_url") projects_with_me = MethodField()
read_only_fields = ("id",)
def validate_username(self, attrs, source):
value = attrs[source]
validator = validators.RegexValidator(re.compile('^[\w.-]+$'), _("invalid username"),
_("invalid"))
try:
validator(value)
except ValidationError:
raise serializers.ValidationError(_("Required. 255 characters or fewer. Letters, "
"numbers and /./-/_ characters'"))
if (self.object and
self.object.username != value and
User.objects.filter(username=value).exists()):
raise serializers.ValidationError(_("Invalid username. Try with a different one."))
return attrs
def get_full_name_display(self, obj): def get_full_name_display(self, obj):
return obj.get_full_name() if obj else "" return obj.get_full_name() if obj else ""
@ -113,24 +94,13 @@ class UserSerializer(serializers.ModelSerializer):
class UserAdminSerializer(UserSerializer): class UserAdminSerializer(UserSerializer):
total_private_projects = serializers.SerializerMethodField("get_total_private_projects") total_private_projects = MethodField()
total_public_projects = serializers.SerializerMethodField("get_total_public_projects") total_public_projects = MethodField()
email = Field()
class Meta: max_private_projects = Field()
model = User max_public_projects = Field()
# IMPORTANT: Maintain the UserSerializer Meta up to date max_memberships_private_projects = Field()
# with this info (including here the email) max_memberships_public_projects = Field()
fields = ("id", "username", "full_name", "full_name_display", "email",
"color", "bio", "lang", "theme", "timezone", "is_active", "photo",
"big_photo", "gravatar_url",
"max_private_projects", "max_public_projects",
"max_memberships_private_projects", "max_memberships_public_projects",
"total_private_projects", "total_public_projects")
read_only_fields = ("id", "email",
"max_private_projects", "max_public_projects",
"max_memberships_private_projects",
"max_memberships_public_projects")
def get_total_private_projects(self, user): def get_total_private_projects(self, user):
return user.owned_projects.filter(is_private=True).count() return user.owned_projects.filter(is_private=True).count()
@ -163,75 +133,63 @@ class UserBasicInfoSerializer(serializers.LightSerializer):
return super().to_value(instance) return super().to_value(instance)
class RecoverySerializer(serializers.Serializer):
token = serializers.CharField(max_length=200)
password = serializers.CharField(min_length=6)
class ChangeEmailSerializer(serializers.Serializer):
email_token = serializers.CharField(max_length=200)
class CancelAccountSerializer(serializers.Serializer):
cancel_token = serializers.CharField(max_length=200)
###################################################### ######################################################
# Role # Role
###################################################### ######################################################
class RoleSerializer(serializers.ModelSerializer): class RoleSerializer(serializers.LightSerializer):
members_count = serializers.SerializerMethodField("get_members_count") id = Field()
name = Field()
computable = Field()
project = Field(attr="project_id")
order = Field()
members_count = MethodField()
permissions = PgArrayField(required=False) permissions = PgArrayField(required=False)
class Meta:
model = Role
fields = ('id', 'name', 'permissions', 'computable', 'project', 'order', 'members_count')
i18n_fields = ("name",)
def get_members_count(self, obj): def get_members_count(self, obj):
return obj.memberships.count() return obj.memberships.count()
class ProjectRoleSerializer(serializers.ModelSerializer): class ProjectRoleSerializer(serializers.LightSerializer):
class Meta: id = Field()
model = Role name = I18NField()
fields = ('id', 'name', 'slug', 'order', 'computable') slug = Field()
i18n_fields = ("name",) order = Field()
computable = Field()
###################################################### ######################################################
# Like # Like
###################################################### ######################################################
class HighLightedContentSerializer(serializers.Serializer): class HighLightedContentSerializer(serializers.LightSerializer):
type = serializers.CharField() type = Field()
id = serializers.IntegerField() id = Field()
ref = serializers.IntegerField() ref = Field()
slug = serializers.CharField() slug = Field()
name = serializers.CharField() name = Field()
subject = serializers.CharField() subject = Field()
description = serializers.SerializerMethodField("get_description") description = MethodField()
assigned_to = serializers.IntegerField() assigned_to = Field()
status = serializers.CharField() status = Field()
status_color = serializers.CharField() status_color = Field()
tags_colors = serializers.SerializerMethodField("get_tags_color") tags_colors = MethodField()
created_date = serializers.DateTimeField() created_date = Field()
is_private = serializers.SerializerMethodField("get_is_private") is_private = MethodField()
logo_small_url = serializers.SerializerMethodField("get_logo_small_url") logo_small_url = MethodField()
project = serializers.SerializerMethodField("get_project") project = MethodField()
project_name = serializers.SerializerMethodField("get_project_name") project_name = MethodField()
project_slug = serializers.SerializerMethodField("get_project_slug") project_slug = MethodField()
project_is_private = serializers.SerializerMethodField("get_project_is_private") project_is_private = MethodField()
project_blocked_code = serializers.CharField() project_blocked_code = Field()
assigned_to_username = serializers.CharField() assigned_to_username = Field()
assigned_to_full_name = serializers.CharField() assigned_to_full_name = Field()
assigned_to_photo = serializers.SerializerMethodField("get_photo") assigned_to_photo = MethodField()
is_watcher = serializers.SerializerMethodField("get_is_watcher") is_watcher = MethodField()
total_watchers = serializers.IntegerField() total_watchers = Field()
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
# Don't pass the extra ids args up to the superclass # Don't pass the extra ids args up to the superclass
@ -241,18 +199,18 @@ class HighLightedContentSerializer(serializers.Serializer):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def _none_if_project(self, obj, property): def _none_if_project(self, obj, property):
type = obj.get("type", "") type = getattr(obj, "type", "")
if type == "project": if type == "project":
return None return None
return obj.get(property) return getattr(obj, property)
def _none_if_not_project(self, obj, property): def _none_if_not_project(self, obj, property):
type = obj.get("type", "") type = getattr(obj, "type", "")
if type != "project": if type != "project":
return None return None
return obj.get(property) return getattr(obj, property)
def get_project(self, obj): def get_project(self, obj):
return self._none_if_project(obj, "project") return self._none_if_project(obj, "project")
@ -278,29 +236,29 @@ class HighLightedContentSerializer(serializers.Serializer):
return get_thumbnail_url(logo, settings.THN_LOGO_SMALL) return get_thumbnail_url(logo, settings.THN_LOGO_SMALL)
return None return None
def get_photo(self, obj): def get_assigned_to_photo(self, obj):
type = obj.get("type", "") type = getattr(obj, "type", "")
if type == "project": if type == "project":
return None return None
UserData = namedtuple("UserData", ["photo", "email"]) UserData = namedtuple("UserData", ["photo", "email"])
user_data = UserData(photo=obj["assigned_to_photo"], email=obj.get("assigned_to_email") or "") user_data = UserData(photo=obj.assigned_to_photo, email=obj.assigned_to_email or "")
return get_photo_or_gravatar_url(user_data) return get_photo_or_gravatar_url(user_data)
def get_tags_color(self, obj): def get_tags_colors(self, obj):
tags = obj.get("tags", []) tags = getattr(obj, "tags", [])
tags = tags if tags is not None else [] tags = tags if tags is not None else []
tags_colors = obj.get("tags_colors", []) tags_colors = getattr(obj, "tags_colors", [])
tags_colors = tags_colors if tags_colors is not None else [] tags_colors = tags_colors if tags_colors is not None else []
return [{"name": tc[0], "color": tc[1]} for tc in tags_colors if tc[0] in tags] return [{"name": tc[0], "color": tc[1]} for tc in tags_colors if tc[0] in tags]
def get_is_watcher(self, obj): def get_is_watcher(self, obj):
return obj["id"] in self.user_watching.get(obj["type"], []) return obj.id in self.user_watching.get(obj.type, [])
class LikedObjectSerializer(HighLightedContentSerializer): class LikedObjectSerializer(HighLightedContentSerializer):
is_fan = serializers.SerializerMethodField("get_is_fan") is_fan = MethodField()
total_fans = serializers.IntegerField() total_fans = Field()
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
# Don't pass the extra ids args up to the superclass # Don't pass the extra ids args up to the superclass
@ -310,12 +268,12 @@ class LikedObjectSerializer(HighLightedContentSerializer):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def get_is_fan(self, obj): def get_is_fan(self, obj):
return obj["id"] in self.user_likes.get(obj["type"], []) return obj.id in self.user_likes.get(obj.type, [])
class VotedObjectSerializer(HighLightedContentSerializer): class VotedObjectSerializer(HighLightedContentSerializer):
is_voter = serializers.SerializerMethodField("get_is_voter") is_voter = MethodField()
total_voters = serializers.IntegerField() total_voters = Field()
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
# Don't pass the extra ids args up to the superclass # Don't pass the extra ids args up to the superclass
@ -325,4 +283,4 @@ class VotedObjectSerializer(HighLightedContentSerializer):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def get_is_voter(self, obj): def get_is_voter(self, obj):
return obj["id"] in self.user_votes.get(obj["type"], []) return obj.id in self.user_votes.get(obj.type, [])

View File

@ -3,7 +3,6 @@
# Copyright (C) 2014-2016 Jesús Espino <jespinog@gmail.com> # Copyright (C) 2014-2016 Jesús Espino <jespinog@gmail.com>
# Copyright (C) 2014-2016 David Barragán <bameda@dbarragan.com> # Copyright (C) 2014-2016 David Barragán <bameda@dbarragan.com>
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net> # Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
# Copyright (C) 2014-2016 Anler Hernández <hello@anler.me>
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the # published by the Free Software Foundation, either version 3 of the
@ -17,17 +16,92 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import ugettext as _ from django.core import validators as core_validators
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
from taiga.base.api import serializers from taiga.base.api import serializers
from taiga.base.api import validators
from taiga.base.fields import PgArrayField, Field
from . import models from .models import User, Role
import re
class RoleExistsValidator: class RoleExistsValidator:
def validate_role_id(self, attrs, source): def validate_role_id(self, attrs, source):
value = attrs[source] value = attrs[source]
if not models.Role.objects.filter(pk=value).exists(): if not Role.objects.filter(pk=value).exists():
msg = _("There's no role with that id") msg = _("There's no role with that id")
raise serializers.ValidationError(msg) raise serializers.ValidationError(msg)
return attrs return attrs
######################################################
# User
######################################################
class UserValidator(validators.ModelValidator):
class Meta:
model = User
fields = ("username", "full_name", "color", "bio", "lang",
"theme", "timezone", "is_active")
def validate_username(self, attrs, source):
value = attrs[source]
validator = core_validators.RegexValidator(re.compile('^[\w.-]+$'), _("invalid username"),
_("invalid"))
try:
validator(value)
except ValidationError:
raise validators.ValidationError(_("Required. 255 characters or fewer. Letters, "
"numbers and /./-/_ characters'"))
if (self.object and
self.object.username != value and
User.objects.filter(username=value).exists()):
raise validators.ValidationError(_("Invalid username. Try with a different one."))
return attrs
class UserAdminValidator(UserValidator):
class Meta:
model = User
# IMPORTANT: Maintain the UserSerializer Meta up to date
# with this info (including here the email)
fields = ("username", "full_name", "color", "bio", "lang",
"theme", "timezone", "is_active", "email")
class RecoveryValidator(validators.Validator):
token = serializers.CharField(max_length=200)
password = serializers.CharField(min_length=6)
class ChangeEmailValidator(validators.Validator):
email_token = serializers.CharField(max_length=200)
class CancelAccountValidator(validators.Validator):
cancel_token = serializers.CharField(max_length=200)
######################################################
# Role
######################################################
class RoleValidator(validators.ModelValidator):
permissions = PgArrayField(required=False)
class Meta:
model = Role
fields = ('id', 'name', 'permissions', 'computable', 'project', 'order')
i18n_fields = ("name",)
class ProjectRoleValidator(validators.ModelValidator):
class Meta:
model = Role
fields = ('id', 'name', 'slug', 'order', 'computable')

View File

@ -30,6 +30,7 @@ from ..utils import DUMMY_BMP_DATA
from taiga.base.utils import json from taiga.base.utils import json
from taiga.base.utils.thumbnails import get_thumbnail_url from taiga.base.utils.thumbnails import get_thumbnail_url
from taiga.base.utils.dicts import into_namedtuple
from taiga.users import models from taiga.users import models
from taiga.users.serializers import LikedObjectSerializer, VotedObjectSerializer from taiga.users.serializers import LikedObjectSerializer, VotedObjectSerializer
from taiga.auth.tokens import get_token_for_user from taiga.auth.tokens import get_token_for_user
@ -505,7 +506,7 @@ def test_get_watched_list_valid_info_for_project():
raw_project_watch_info = get_watched_list(fav_user, viewer_user)[0] raw_project_watch_info = get_watched_list(fav_user, viewer_user)[0]
project_watch_info = LikedObjectSerializer(raw_project_watch_info).data project_watch_info = LikedObjectSerializer(into_namedtuple(raw_project_watch_info)).data
assert project_watch_info["type"] == "project" assert project_watch_info["type"] == "project"
assert project_watch_info["id"] == project.id assert project_watch_info["id"] == project.id
@ -559,7 +560,7 @@ def test_get_liked_list_valid_info():
project.refresh_totals() project.refresh_totals()
raw_project_like_info = get_liked_list(fan_user, viewer_user)[0] raw_project_like_info = get_liked_list(fan_user, viewer_user)[0]
project_like_info = LikedObjectSerializer(raw_project_like_info).data project_like_info = LikedObjectSerializer(into_namedtuple(raw_project_like_info)).data
assert project_like_info["type"] == "project" assert project_like_info["type"] == "project"
assert project_like_info["id"] == project.id assert project_like_info["id"] == project.id
@ -609,7 +610,7 @@ def test_get_watched_list_valid_info_for_not_project_types():
instance.add_watcher(fav_user) instance.add_watcher(fav_user)
raw_instance_watch_info = get_watched_list(fav_user, viewer_user, type=object_type)[0] raw_instance_watch_info = get_watched_list(fav_user, viewer_user, type=object_type)[0]
instance_watch_info = VotedObjectSerializer(raw_instance_watch_info).data instance_watch_info = VotedObjectSerializer(into_namedtuple(raw_instance_watch_info)).data
assert instance_watch_info["type"] == object_type assert instance_watch_info["type"] == object_type
assert instance_watch_info["id"] == instance.id assert instance_watch_info["id"] == instance.id
@ -666,7 +667,7 @@ def test_get_voted_list_valid_info():
f.VotesFactory(content_type=content_type, object_id=instance.id, count=3) f.VotesFactory(content_type=content_type, object_id=instance.id, count=3)
raw_instance_vote_info = get_voted_list(fav_user, viewer_user, type=object_type)[0] raw_instance_vote_info = get_voted_list(fav_user, viewer_user, type=object_type)[0]
instance_vote_info = VotedObjectSerializer(raw_instance_vote_info).data instance_vote_info = VotedObjectSerializer(into_namedtuple(raw_instance_vote_info)).data
assert instance_vote_info["type"] == object_type assert instance_vote_info["type"] == object_type
assert instance_vote_info["id"] == instance.id assert instance_vote_info["id"] == instance.id