From 0571baa63c039be061ddf448a8f0b0d3b24ea32e Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Tue, 2 Jun 2020 21:12:01 +0100 Subject: Convert the roles field on the user model from a many-to-many field to a postgres array with roles --- pydis_site/apps/api/models/bot/user.py | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) (limited to 'pydis_site/apps/api') diff --git a/pydis_site/apps/api/models/bot/user.py b/pydis_site/apps/api/models/bot/user.py index 65e8751e..cc4e7873 100644 --- a/pydis_site/apps/api/models/bot/user.py +++ b/pydis_site/apps/api/models/bot/user.py @@ -1,10 +1,22 @@ +from django.contrib.postgres.fields import ArrayField from django.core.validators import MaxValueValidator, MinValueValidator +from django.core.exceptions import ValidationError from django.db import models from pydis_site.apps.api.models.bot.role import Role from pydis_site.apps.api.models.utils import ModelReprMixin +def _validate_existing_role(value: int) -> None: + """Validate that a role exists when given in to the user model.""" + role = Role.objects.filter(id=value) + + if not role: + raise ValidationError( + f"Role with ID {value} does not exist" + ) + + class User(ModelReprMixin, models.Model): """A Discord user.""" @@ -31,9 +43,18 @@ class User(ModelReprMixin, models.Model): ), help_text="The discriminator of this user, taken from Discord." ) - roles = models.ManyToManyField( - Role, - help_text="Any roles this user has on our server." + roles = ArrayField( + models.BigIntegerField( + validators=( + MinValueValidator( + limit_value=0, + message="Role IDs cannot be negative." + ), + _validate_existing_role + ) + ), + default=list, + help_text="IDs of roles the user has on the server" ) in_guild = models.BooleanField( default=True, @@ -51,7 +72,7 @@ class User(ModelReprMixin, models.Model): This will fall back to the Developers role if the user does not have any roles. """ - roles = self.roles.all() + roles = Role.objects.filter(id__in=self.roles) if not roles: return Role.objects.get(name="Developers") - return max(self.roles.all()) + return max(roles) -- cgit v1.2.3 From fac0d15afade7b00e883e3f9798a846956f222cc Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Tue, 2 Jun 2020 21:13:01 +0100 Subject: Add migrations to switch user field to array --- .../api/migrations/0053_user_roles_to_array.py | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 pydis_site/apps/api/migrations/0053_user_roles_to_array.py (limited to 'pydis_site/apps/api') diff --git a/pydis_site/apps/api/migrations/0053_user_roles_to_array.py b/pydis_site/apps/api/migrations/0053_user_roles_to_array.py new file mode 100644 index 00000000..7ff3a548 --- /dev/null +++ b/pydis_site/apps/api/migrations/0053_user_roles_to_array.py @@ -0,0 +1,24 @@ +# Generated by Django 2.2.11 on 2020-06-02 13:42 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0052_remove_user_avatar_hash'), + ] + + operations = [ + migrations.RemoveField( + model_name='user', + name='roles', + ), + migrations.AddField( + model_name='user', + name='roles', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.')]), default=list, help_text='IDs of roles the user has on the server', size=None), + ), + ] -- cgit v1.2.3 From 044fb2661de97c710c37e48f908d99a4b173a264 Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Tue, 2 Jun 2020 21:13:19 +0100 Subject: Add validator to ensure roles passed to user model exist --- .../migrations/0054_user_invalidate_unknown_role.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py (limited to 'pydis_site/apps/api') diff --git a/pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py b/pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py new file mode 100644 index 00000000..96230015 --- /dev/null +++ b/pydis_site/apps/api/migrations/0054_user_invalidate_unknown_role.py @@ -0,0 +1,21 @@ +# Generated by Django 2.2.11 on 2020-06-02 20:08 + +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models +import pydis_site.apps.api.models.bot.user + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0053_user_roles_to_array'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='roles', + field=django.contrib.postgres.fields.ArrayField(base_field=models.BigIntegerField(validators=[django.core.validators.MinValueValidator(limit_value=0, message='Role IDs cannot be negative.'), pydis_site.apps.api.models.bot.user._validate_existing_role]), default=list, help_text='IDs of roles the user has on the server', size=None), + ), + ] -- cgit v1.2.3 From 967ac5cb20280d4b3d70e9c9d15b435475cd70df Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Tue, 2 Jun 2020 21:13:30 +0100 Subject: Remove primary key from user serializer --- pydis_site/apps/api/serializers.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'pydis_site/apps/api') diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py index cc3f167d..f2d5144c 100644 --- a/pydis_site/apps/api/serializers.py +++ b/pydis_site/apps/api/serializers.py @@ -229,8 +229,6 @@ class TagSerializer(ModelSerializer): class UserSerializer(BulkSerializerMixin, ModelSerializer): """A class providing (de-)serialization of `User` instances.""" - roles = PrimaryKeyRelatedField(many=True, queryset=Role.objects.all(), required=False) - class Meta: """Metadata defined for the Django REST Framework.""" -- cgit v1.2.3 From 3cbe2b173db184d655939fd6d83fee4870b7da49 Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Tue, 2 Jun 2020 21:14:28 +0100 Subject: Alter API tests to use new user roles format --- pydis_site/apps/api/tests/test_users.py | 2 +- pydis_site/apps/api/viewsets/bot/user.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'pydis_site/apps/api') diff --git a/pydis_site/apps/api/tests/test_users.py b/pydis_site/apps/api/tests/test_users.py index 4f563dc6..4c0f6e27 100644 --- a/pydis_site/apps/api/tests/test_users.py +++ b/pydis_site/apps/api/tests/test_users.py @@ -146,7 +146,7 @@ class UserModelTests(APISubdomainTestCase): discriminator=1111, in_guild=True, ) - cls.user_with_roles.roles.add(cls.role_bottom, cls.role_top) + cls.user_with_roles.roles.extend([cls.role_bottom.id, cls.role_top.id]) cls.user_without_roles = User.objects.create( id=2, diff --git a/pydis_site/apps/api/viewsets/bot/user.py b/pydis_site/apps/api/viewsets/bot/user.py index 8f5bccfa..9571b3d7 100644 --- a/pydis_site/apps/api/viewsets/bot/user.py +++ b/pydis_site/apps/api/viewsets/bot/user.py @@ -118,4 +118,4 @@ class UserViewSet(BulkCreateModelMixin, ModelViewSet): """ serializer_class = UserSerializer - queryset = User.objects.prefetch_related('roles') + queryset = User.objects -- cgit v1.2.3 From f992f4b2900716ce5085aae1cb6fd104b0e6fd38 Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Tue, 2 Jun 2020 21:20:58 +0100 Subject: Alter import order in user model --- pydis_site/apps/api/models/bot/user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pydis_site/apps/api') diff --git a/pydis_site/apps/api/models/bot/user.py b/pydis_site/apps/api/models/bot/user.py index cc4e7873..c7510d8c 100644 --- a/pydis_site/apps/api/models/bot/user.py +++ b/pydis_site/apps/api/models/bot/user.py @@ -1,6 +1,6 @@ from django.contrib.postgres.fields import ArrayField -from django.core.validators import MaxValueValidator, MinValueValidator from django.core.exceptions import ValidationError +from django.core.validators import MaxValueValidator, MinValueValidator from django.db import models from pydis_site.apps.api.models.bot.role import Role -- cgit v1.2.3 From 56fabe78d27286b431d391cf63e1adabdb8787a0 Mon Sep 17 00:00:00 2001 From: Joseph Banks Date: Fri, 5 Jun 2020 12:48:06 +0100 Subject: Remove very generous newline in role validator Co-authored-by: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> --- pydis_site/apps/api/models/bot/user.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'pydis_site/apps/api') diff --git a/pydis_site/apps/api/models/bot/user.py b/pydis_site/apps/api/models/bot/user.py index c7510d8c..bff4d642 100644 --- a/pydis_site/apps/api/models/bot/user.py +++ b/pydis_site/apps/api/models/bot/user.py @@ -12,9 +12,7 @@ def _validate_existing_role(value: int) -> None: role = Role.objects.filter(id=value) if not role: - raise ValidationError( - f"Role with ID {value} does not exist" - ) + raise ValidationError(f"Role with ID {value} does not exist") class User(ModelReprMixin, models.Model): -- cgit v1.2.3