From ec2ae9d1ef38d44cfeeb4abbe3bda035afe93e2b Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Fri, 4 Oct 2019 08:13:18 +0200 Subject: Add validation rules to Infraction serializer https://github.com/python-discord/site/issues/273 This commit adds validation rules to the Infraction serializer that validate if a given infraction should be accepted based on its status of being considered `active`. If the validation fails, the API will reject the request and return a 400 status. Specifically, this validator checks that: - infractions that can never be active do not have `active=True` set; - a user can never receive a second active infraction of the same type. Tests have been added to `test_infractions.py` to ensure that the validators work as expected. This commit implements the first part of #273 --- pydis_site/apps/api/models/bot/user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/models/bot/user.py b/pydis_site/apps/api/models/bot/user.py index 21617dc4..5140d2bf 100644 --- a/pydis_site/apps/api/models/bot/user.py +++ b/pydis_site/apps/api/models/bot/user.py @@ -50,7 +50,7 @@ class User(ModelReprMixin, models.Model): def __str__(self): """Returns the name and discriminator for the current user, for display purposes.""" - return f"{self.name}#{self.discriminator}" + return f"{self.name}#{self.discriminator:0>4}" @property def top_role(self) -> Role: -- cgit v1.2.3 From 0d383cbe925fdec97cb678c0168ecf7e90d3d729 Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Tue, 8 Oct 2019 01:22:15 +0200 Subject: Prevent double active infractions with constraint https://github.com/python-discord/site/issues/273 This commits adds a UniqueConstraint for active infractions on a combination of the `user` and `type` field. This means that a user can only have one active infraction of a given type in the database at any time. I've also added tests to make sure that this behaves as expected. --- ...45_add_infractions_unique_constraints_active.py | 17 +++++ pydis_site/apps/api/models/bot/infraction.py | 7 ++ pydis_site/apps/api/tests/test_infractions.py | 89 +++++++++++++++++++++- 3 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 pydis_site/apps/api/migrations/0045_add_infractions_unique_constraints_active.py (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/migrations/0045_add_infractions_unique_constraints_active.py b/pydis_site/apps/api/migrations/0045_add_infractions_unique_constraints_active.py new file mode 100644 index 00000000..bacb56b8 --- /dev/null +++ b/pydis_site/apps/api/migrations/0045_add_infractions_unique_constraints_active.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.6 on 2019-10-07 18:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0044_active_infractions_migration'), + ] + + operations = [ + migrations.AddConstraint( + model_name='infraction', + constraint=models.UniqueConstraint(condition=models.Q(active=True), fields=('user', 'type'), name='unique_active_infraction_per_type_per_user'), + ), + ] diff --git a/pydis_site/apps/api/models/bot/infraction.py b/pydis_site/apps/api/models/bot/infraction.py index dfb32a97..108fd3a2 100644 --- a/pydis_site/apps/api/models/bot/infraction.py +++ b/pydis_site/apps/api/models/bot/infraction.py @@ -71,3 +71,10 @@ class Infraction(ModelReprMixin, models.Model): """Defines the meta options for the infraction model.""" ordering = ['-inserted_at'] + constraints = ( + models.UniqueConstraint( + fields=["user", "type"], + condition=models.Q(active=True), + name="unique_active_infraction_per_type_per_user" + ), + ) diff --git a/pydis_site/apps/api/tests/test_infractions.py b/pydis_site/apps/api/tests/test_infractions.py index 8279b6a6..69d9ebea 100644 --- a/pydis_site/apps/api/tests/test_infractions.py +++ b/pydis_site/apps/api/tests/test_infractions.py @@ -1,6 +1,7 @@ from datetime import datetime as dt, timedelta, timezone from urllib.parse import quote +from django.db.utils import IntegrityError from django_hosts.resolvers import reverse from .base import APISubdomainTestCase @@ -167,6 +168,12 @@ class CreationTests(APISubdomainTestCase): discriminator=1, avatar_hash=None ) + cls.second_user = User.objects.create( + id=6, + name='carl', + discriminator=2, + avatar_hash=None + ) def test_accepts_valid_data(self): url = reverse('bot:infraction-list', host='api') @@ -390,6 +397,82 @@ class CreationTests(APISubdomainTestCase): second_response = self.client.post(url, data=second_active_infraction) self.assertEqual(second_response.status_code, 201) + def test_unique_constraint_raises_integrity_error_on_second_active_of_same_type(self): + """Do we raise `IntegrityError` for the second active infraction of a type for a user?""" + Infraction.objects.create( + user=self.user, + actor=self.user, + type="ban", + active=True, + reason="The first active ban" + ) + with self.assertRaises(IntegrityError): + Infraction.objects.create( + user=self.user, + actor=self.user, + type="ban", + active=True, + reason="The second active ban" + ) + + def test_unique_constraint_accepts_active_infraction_after_inactive_infraction(self): + """Do we accept an active infraction if the others of the same type are inactive?""" + Infraction.objects.create( + user=self.user, + actor=self.user, + type="ban", + active=False, + reason="The first inactive ban" + ) + Infraction.objects.create( + user=self.user, + actor=self.user, + type="ban", + active=False, + reason="The second inactive ban" + ) + Infraction.objects.create( + user=self.user, + actor=self.user, + type="ban", + active=True, + reason="The first active ban" + ) + + def test_unique_constraint_accepts_second_active_of_different_type(self): + """Do we accept a second active infraction of a different type for a given user?""" + Infraction.objects.create( + user=self.user, + actor=self.user, + type="ban", + active=True, + reason="The first active ban" + ) + Infraction.objects.create( + user=self.user, + actor=self.user, + type="mute", + active=True, + reason="The first active mute" + ) + + def test_unique_constraint_accepts_active_infractions_for_different_users(self): + """Do we accept two active infractions of the same type for two different users?""" + Infraction.objects.create( + user=self.user, + actor=self.user, + type="ban", + active=True, + reason="An active ban for the first user" + ) + Infraction.objects.create( + user=self.second_user, + actor=self.second_user, + type="ban", + active=False, + reason="An active ban for the second user" + ) + class ExpandedTests(APISubdomainTestCase): @classmethod @@ -403,12 +486,14 @@ class ExpandedTests(APISubdomainTestCase): cls.kick = Infraction.objects.create( user_id=cls.user.id, actor_id=cls.user.id, - type='kick' + type='kick', + active=False ) cls.warning = Infraction.objects.create( user_id=cls.user.id, actor_id=cls.user.id, - type='warning' + type='warning', + active=False, ) def check_expanded_fields(self, infraction): -- cgit v1.2.3 From 731a3ca4cefd7ed9bc6619b0d98cb0c28f14f290 Mon Sep 17 00:00:00 2001 From: Akarys42 Date: Tue, 22 Oct 2019 18:05:09 +0200 Subject: Create an OffensiveMessage model This model will be used to store message that triggered a filter and that will be deleted one week after it was sent. --- .../apps/api/migrations/0046_offensivemessage.py | 25 ++++++++ pydis_site/apps/api/models/__init__.py | 1 + pydis_site/apps/api/models/bot/__init__.py | 1 + .../apps/api/models/bot/offensive_message.py | 44 +++++++++++++++ pydis_site/apps/api/serializers.py | 11 ++++ pydis_site/apps/api/urls.py | 7 ++- pydis_site/apps/api/viewsets/__init__.py | 1 + pydis_site/apps/api/viewsets/bot/__init__.py | 1 + .../apps/api/viewsets/bot/offensive_message.py | 66 ++++++++++++++++++++++ 9 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 pydis_site/apps/api/migrations/0046_offensivemessage.py create mode 100644 pydis_site/apps/api/models/bot/offensive_message.py create mode 100644 pydis_site/apps/api/viewsets/bot/offensive_message.py (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/migrations/0046_offensivemessage.py b/pydis_site/apps/api/migrations/0046_offensivemessage.py new file mode 100644 index 00000000..0ce65946 --- /dev/null +++ b/pydis_site/apps/api/migrations/0046_offensivemessage.py @@ -0,0 +1,25 @@ +# Generated by Django 2.2.6 on 2019-10-24 17:57 + +import django.core.validators +from django.db import migrations, models +import pydis_site.apps.api.models.bot.offensive_message +import pydis_site.apps.api.models.utils + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0045_add_plural_name_for_log_entry'), + ] + + operations = [ + migrations.CreateModel( + name='OffensiveMessage', + fields=[ + ('id', models.BigIntegerField(help_text='The message ID as taken from Discord.', primary_key=True, serialize=False, validators=[django.core.validators.MinValueValidator(limit_value=0, message='Message IDs cannot be negative.')])), + ('channel_id', models.BigIntegerField(help_text='The channel ID that the message was sent in, taken from Discord.', validators=[django.core.validators.MinValueValidator(limit_value=0, message='Channel IDs cannot be negative.')])), + ('delete_date', models.DateField(help_text='The date on which the message will be auto-deleted.', validators=[pydis_site.apps.api.models.bot.offensive_message.future_date_validator])), + ], + bases=(pydis_site.apps.api.models.utils.ModelReprMixin, models.Model), + ), + ] diff --git a/pydis_site/apps/api/models/__init__.py b/pydis_site/apps/api/models/__init__.py index a4656bc3..450d18cd 100644 --- a/pydis_site/apps/api/models/__init__.py +++ b/pydis_site/apps/api/models/__init__.py @@ -7,6 +7,7 @@ from .bot import ( Message, MessageDeletionContext, Nomination, + OffensiveMessage, OffTopicChannelName, Reminder, Role, diff --git a/pydis_site/apps/api/models/bot/__init__.py b/pydis_site/apps/api/models/bot/__init__.py index 46219ea2..8ae47746 100644 --- a/pydis_site/apps/api/models/bot/__init__.py +++ b/pydis_site/apps/api/models/bot/__init__.py @@ -7,6 +7,7 @@ from .message import Message from .message_deletion_context import MessageDeletionContext from .nomination import Nomination from .off_topic_channel_name import OffTopicChannelName +from .offensive_message import OffensiveMessage from .reminder import Reminder from .role import Role from .tag import Tag diff --git a/pydis_site/apps/api/models/bot/offensive_message.py b/pydis_site/apps/api/models/bot/offensive_message.py new file mode 100644 index 00000000..523fd482 --- /dev/null +++ b/pydis_site/apps/api/models/bot/offensive_message.py @@ -0,0 +1,44 @@ +import datetime + +from django.core.exceptions import ValidationError +from django.core.validators import MinValueValidator +from django.db import models + +from pydis_site.apps.api.models.utils import ModelReprMixin + + +def future_date_validator(date: datetime.date) -> None: + """Raise ValidationError if the date isn't a future date.""" + if date < datetime.date.today(): + raise ValidationError("Date must be a future date") + + +class OffensiveMessage(ModelReprMixin, models.Model): + """A message that triggered a filter and that will be deleted one week after it was sent.""" + + id = models.BigIntegerField( + primary_key=True, + help_text="The message ID as taken from Discord.", + validators=( + MinValueValidator( + limit_value=0, + message="Message IDs cannot be negative." + ), + ) + ) + channel_id = models.BigIntegerField( + help_text=( + "The channel ID that the message was " + "sent in, taken from Discord." + ), + validators=( + MinValueValidator( + limit_value=0, + message="Channel IDs cannot be negative." + ), + ) + ) + delete_date = models.DateField( + help_text="The date on which the message will be auto-deleted.", + validators=(future_date_validator,) + ) diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py index 326e20e1..4ef7ec78 100644 --- a/pydis_site/apps/api/serializers.py +++ b/pydis_site/apps/api/serializers.py @@ -8,6 +8,7 @@ from .models import ( DocumentationLink, Infraction, LogEntry, MessageDeletionContext, Nomination, OffTopicChannelName, + OffensiveMessage, Reminder, Role, Tag, User ) @@ -236,3 +237,13 @@ class NominationSerializer(ModelSerializer): fields = ( 'id', 'active', 'actor', 'reason', 'user', 'inserted_at', 'end_reason', 'ended_at') + + +class OffensiveMessageSerializer(ModelSerializer): + """A class providing (de-)serialization of `OffensiveMessage` instances.""" + + class Meta: + """Metadata defined for the Django REST Framework.""" + + model = OffensiveMessage + fields = ('id', 'channel_id', 'delete_date') diff --git a/pydis_site/apps/api/urls.py b/pydis_site/apps/api/urls.py index ac6704c8..b1864ba7 100644 --- a/pydis_site/apps/api/urls.py +++ b/pydis_site/apps/api/urls.py @@ -6,7 +6,8 @@ from .viewsets import ( BotSettingViewSet, DeletedMessageViewSet, DocumentationLinkViewSet, InfractionViewSet, LogEntryViewSet, NominationViewSet, - OffTopicChannelNameViewSet, ReminderViewSet, + OffTopicChannelNameViewSet, + OffensiveMessageViewSet, ReminderViewSet, RoleViewSet, TagViewSet, UserViewSet ) @@ -33,6 +34,10 @@ bot_router.register( 'nominations', NominationViewSet ) +bot_router.register( + 'offensive-message', + OffensiveMessageViewSet +) bot_router.register( 'off-topic-channel-names', OffTopicChannelNameViewSet, diff --git a/pydis_site/apps/api/viewsets/__init__.py b/pydis_site/apps/api/viewsets/__init__.py index f9a186d9..3cf9f641 100644 --- a/pydis_site/apps/api/viewsets/__init__.py +++ b/pydis_site/apps/api/viewsets/__init__.py @@ -5,6 +5,7 @@ from .bot import ( DocumentationLinkViewSet, InfractionViewSet, NominationViewSet, + OffensiveMessageViewSet, OffTopicChannelNameViewSet, ReminderViewSet, RoleViewSet, diff --git a/pydis_site/apps/api/viewsets/bot/__init__.py b/pydis_site/apps/api/viewsets/bot/__init__.py index f1851e32..b3e0fa4d 100644 --- a/pydis_site/apps/api/viewsets/bot/__init__.py +++ b/pydis_site/apps/api/viewsets/bot/__init__.py @@ -5,6 +5,7 @@ from .documentation_link import DocumentationLinkViewSet from .infraction import InfractionViewSet from .nomination import NominationViewSet from .off_topic_channel_name import OffTopicChannelNameViewSet +from .offensive_message import OffensiveMessageViewSet from .reminder import ReminderViewSet from .role import RoleViewSet from .tag import TagViewSet diff --git a/pydis_site/apps/api/viewsets/bot/offensive_message.py b/pydis_site/apps/api/viewsets/bot/offensive_message.py new file mode 100644 index 00000000..cd200efe --- /dev/null +++ b/pydis_site/apps/api/viewsets/bot/offensive_message.py @@ -0,0 +1,66 @@ +from django_filters.rest_framework import DjangoFilterBackend +from rest_framework.filters import SearchFilter +from rest_framework.mixins import ( + CreateModelMixin, + DestroyModelMixin, + ListModelMixin, + UpdateModelMixin +) +from rest_framework.viewsets import GenericViewSet + +from pydis_site.apps.api.models.bot.offensive_message import OffensiveMessage +from pydis_site.apps.api.serializers import OffensiveMessageSerializer + + +class OffensiveMessageViewSet( + CreateModelMixin, ListModelMixin, DestroyModelMixin, UpdateModelMixin, GenericViewSet +): + """ + View providing CRUD access to reminders. + + ## Routes + ### GET /bot/offensive-message + Returns all offensive messages in the database. + + #### Response format + >>> [ + ... { + ... 'id': '631953598091100200', + ... 'channel_id': '291284109232308226', + ... 'delete_date': '2020-01-01' + ... }, + ... ... + ... ] + + #### Status codes + - 200: returned on success + + ### POST /bot/offensive-message + Create a new offensive message object. + + #### Request body + >>> { + ... 'id': int, + ... 'channel_id': int, + ... 'delete_date': datetime.date # ISO-8601-formatted date + ... } + + #### Status codes + - 201: returned on success + - 400: if the body format is invalid + + ### DELETE /bot/offensive-message/ + Delete the offensive message object with the given `id`. + + #### Status codes + - 204: returned on success + - 404: if a offensive message object with the given `id` does not exist + + ## Authentication + Requires an API token. + """ + + serializer_class = OffensiveMessageSerializer + queryset = OffensiveMessage.objects.all() + filter_backends = (DjangoFilterBackend, SearchFilter) + filter_fields = ('delete_date',) -- cgit v1.2.3 From f42c936bddc43b30a8245306c86b859185b5e913 Mon Sep 17 00:00:00 2001 From: Akarys42 Date: Fri, 25 Oct 2019 19:16:21 +0200 Subject: Change OffensiveMessage.delete_date from datetime.date to datetime.datetime --- pydis_site/apps/api/models/bot/offensive_message.py | 4 ++-- pydis_site/apps/api/tests/test_validators.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/models/bot/offensive_message.py b/pydis_site/apps/api/models/bot/offensive_message.py index 523fd482..cee5b32c 100644 --- a/pydis_site/apps/api/models/bot/offensive_message.py +++ b/pydis_site/apps/api/models/bot/offensive_message.py @@ -9,7 +9,7 @@ from pydis_site.apps.api.models.utils import ModelReprMixin def future_date_validator(date: datetime.date) -> None: """Raise ValidationError if the date isn't a future date.""" - if date < datetime.date.today(): + if date < datetime.datetime.utcnow(): raise ValidationError("Date must be a future date") @@ -38,7 +38,7 @@ class OffensiveMessage(ModelReprMixin, models.Model): ), ) ) - delete_date = models.DateField( + delete_date = models.DateTimeField( help_text="The date on which the message will be auto-deleted.", validators=(future_date_validator,) ) diff --git a/pydis_site/apps/api/tests/test_validators.py b/pydis_site/apps/api/tests/test_validators.py index 6e86759c..9d993839 100644 --- a/pydis_site/apps/api/tests/test_validators.py +++ b/pydis_site/apps/api/tests/test_validators.py @@ -1,4 +1,4 @@ -from datetime import date +from datetime import datetime from django.core.exceptions import ValidationError from django.test import TestCase @@ -252,8 +252,8 @@ class TagEmbedValidatorTests(TestCase): class OffensiveMessageValidatorsTests(TestCase): def test_accepts_future_date(self): - future_date_validator(date(3000, 1, 1)) + future_date_validator(datetime(3000, 1, 1)) def test_rejects_non_future_date(self): with self.assertRaises(ValidationError): - future_date_validator(date(1000, 1, 1)) + future_date_validator(datetime(1000, 1, 1)) -- cgit v1.2.3 From 6334fd1d7e57f39ed61c90d2bace6b367b676d2f Mon Sep 17 00:00:00 2001 From: Akarys42 Date: Fri, 25 Oct 2019 23:36:31 +0200 Subject: Fix type error in the future date validator --- pydis_site/apps/api/models/bot/offensive_message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/models/bot/offensive_message.py b/pydis_site/apps/api/models/bot/offensive_message.py index cee5b32c..c4080c1f 100644 --- a/pydis_site/apps/api/models/bot/offensive_message.py +++ b/pydis_site/apps/api/models/bot/offensive_message.py @@ -9,7 +9,7 @@ from pydis_site.apps.api.models.utils import ModelReprMixin def future_date_validator(date: datetime.date) -> None: """Raise ValidationError if the date isn't a future date.""" - if date < datetime.datetime.utcnow(): + if date < datetime.datetime.now(datetime.timezone.utc): raise ValidationError("Date must be a future date") -- cgit v1.2.3 From c727d4ce14ae94f0b60402c603e4129a8efe6ef8 Mon Sep 17 00:00:00 2001 From: Akarys42 Date: Sat, 26 Oct 2019 11:14:55 +0200 Subject: Add the offensive message model to the admin panel --- pydis_site/apps/api/admin.py | 2 ++ pydis_site/apps/api/models/bot/offensive_message.py | 4 ++++ 2 files changed, 6 insertions(+) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/admin.py b/pydis_site/apps/api/admin.py index 059f52eb..0333fefc 100644 --- a/pydis_site/apps/api/admin.py +++ b/pydis_site/apps/api/admin.py @@ -12,6 +12,7 @@ from .models import ( MessageDeletionContext, Nomination, OffTopicChannelName, + OffensiveMessage, Role, Tag, User @@ -60,6 +61,7 @@ admin.site.register(Infraction) admin.site.register(LogEntry, LogEntryAdmin) admin.site.register(MessageDeletionContext) admin.site.register(Nomination) +admin.site.register(OffensiveMessage) admin.site.register(OffTopicChannelName) admin.site.register(Role) admin.site.register(Tag) diff --git a/pydis_site/apps/api/models/bot/offensive_message.py b/pydis_site/apps/api/models/bot/offensive_message.py index c4080c1f..b466d9c2 100644 --- a/pydis_site/apps/api/models/bot/offensive_message.py +++ b/pydis_site/apps/api/models/bot/offensive_message.py @@ -42,3 +42,7 @@ class OffensiveMessage(ModelReprMixin, models.Model): help_text="The date on which the message will be auto-deleted.", validators=(future_date_validator,) ) + + def __str__(self): + """Return some info on this message, for display purposes only.""" + return f"Message {self.id}, will be deleted at {self.delete_date}" -- cgit v1.2.3 From ee3f4f937c99ef3db92afc2fcba746003ec694f0 Mon Sep 17 00:00:00 2001 From: Akarys42 Date: Mon, 28 Oct 2019 18:20:07 +0100 Subject: Add a attachments field to the message model --- pydis_site/apps/api/models/bot/message.py | 6 ++++++ pydis_site/apps/api/serializers.py | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/models/bot/message.py b/pydis_site/apps/api/models/bot/message.py index 31316a01..72b0b61a 100644 --- a/pydis_site/apps/api/models/bot/message.py +++ b/pydis_site/apps/api/models/bot/message.py @@ -51,6 +51,12 @@ class Message(ModelReprMixin, models.Model): ), help_text="Embeds attached to this message." ) + attachments = pgfields.ArrayField( + models.URLField( + max_length=512 + ), + help_text="Attachments attached to this message." + ) @property def timestamp(self) -> datetime: diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py index 8a605612..e091ff4f 100644 --- a/pydis_site/apps/api/serializers.py +++ b/pydis_site/apps/api/serializers.py @@ -49,7 +49,8 @@ class DeletedMessageSerializer(ModelSerializer): fields = ( 'id', 'author', 'channel_id', 'content', - 'embeds', 'deletion_context' + 'embeds', 'deletion_context', + 'attachments' ) -- cgit v1.2.3 From 904edb20f694c40435ba27f5a96f759dbf87f3e3 Mon Sep 17 00:00:00 2001 From: Matteo Bertucci Date: Thu, 31 Oct 2019 13:16:15 +0100 Subject: Allow empty attachment field --- .../api/migrations/0048_alter_deletedmessage_api.py | 19 +++++++++++++++++++ pydis_site/apps/api/models/bot/message.py | 1 + 2 files changed, 20 insertions(+) create mode 100644 pydis_site/apps/api/migrations/0048_alter_deletedmessage_api.py (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/migrations/0048_alter_deletedmessage_api.py b/pydis_site/apps/api/migrations/0048_alter_deletedmessage_api.py new file mode 100644 index 00000000..364f57b1 --- /dev/null +++ b/pydis_site/apps/api/migrations/0048_alter_deletedmessage_api.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.6 on 2019-10-31 12:14 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0047_deletedmessage_attachments'), + ] + + operations = [ + migrations.AlterField( + model_name='deletedmessage', + name='attachments', + field=django.contrib.postgres.fields.ArrayField(base_field=models.URLField(max_length=512), blank=True, help_text='Attachments attached to this message.', size=None), + ), + ] diff --git a/pydis_site/apps/api/models/bot/message.py b/pydis_site/apps/api/models/bot/message.py index 72b0b61a..8b18fc9f 100644 --- a/pydis_site/apps/api/models/bot/message.py +++ b/pydis_site/apps/api/models/bot/message.py @@ -55,6 +55,7 @@ class Message(ModelReprMixin, models.Model): models.URLField( max_length=512 ), + blank=True, help_text="Attachments attached to this message." ) -- cgit v1.2.3