From 8f82d0f0807866348a1da5c43c59b7ca80cc77b2 Mon Sep 17 00:00:00 2001 From: Filipe Pina Date: Sat, 21 Jan 2023 17:12:55 +0000 Subject: [PATCH] EventRef to the rescue of typos and manual data migrations! --- notifications/apps.py | 10 +++++++ notifications/models.py | 27 +++++++++++++++++++ testapp/requirements.txt | 2 +- .../management/commands/brute_notify.py | 8 +++--- testapp/testapp/models.py | 3 +++ 5 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 testapp/testapp/models.py diff --git a/notifications/apps.py b/notifications/apps.py index a8ee669..fee6648 100644 --- a/notifications/apps.py +++ b/notifications/apps.py @@ -1,5 +1,6 @@ from django.apps import AppConfig from django.conf import settings +from django.db.models.signals import post_migrate # IMPORT do not use "app_settings.py strategy" as that is not compatible with @override_settings (unittests) # this strategy is @@ -19,3 +20,12 @@ def ready(self): _k = 'NOTIFICATIONS_%s' % k if not hasattr(settings, _k): setattr(settings, _k, v) + + post_migrate.connect(create_events, sender=self) + + +def create_events(sender, **kwargs): + from .models import EventRef, Event + + for ref in EventRef.get_references(): + Event.objects.update_or_create(name=ref.name, defaults={'description': ref.description}) diff --git a/notifications/models.py b/notifications/models.py index 3768693..d8c30c2 100644 --- a/notifications/models.py +++ b/notifications/models.py @@ -4,6 +4,33 @@ from django.template.defaultfilters import truncatechars +class EventRef: + """ + EventRef is not a model but instead a reference to current (or future, if not yet migrated) Event. + This allows hardcoded events to be statically referenced and automatically provisioned + (instead of requiring a data-only migration or the event to be manually added before code is used) + """ + + __refs = [] + + def __init__(self, name, description): + self.__class__.__refs.append(self) + self.name = name + self.description = description + + def __repr__(self) -> str: + # to be able to use EventRef directly in Event.name queries + return self.name + + def get_event(self) -> 'Event': + Event.objects.get(name=self) + + @classmethod + def get_references(cls): + # return a copy of the references + return cls.__refs[:] + + class Event(models.Model): name = models.CharField(max_length=50, primary_key=True) description = models.TextField(blank=True) diff --git a/testapp/requirements.txt b/testapp/requirements.txt index 3ebbd1b..c9680e0 100644 --- a/testapp/requirements.txt +++ b/testapp/requirements.txt @@ -2,4 +2,4 @@ pytest==6.2.5 pytest-cov==2.12.1 pytest-django==4.4.0 -black==20.8b1 +black==22.12.0 diff --git a/testapp/testapp/management/commands/brute_notify.py b/testapp/testapp/management/commands/brute_notify.py index ae0c8e5..dd646a2 100644 --- a/testapp/testapp/management/commands/brute_notify.py +++ b/testapp/testapp/management/commands/brute_notify.py @@ -2,6 +2,7 @@ from django.core.management import call_command from notifications import utils, models, blocks +from testapp.models import TEST_EVENT class Command(BaseCommand): @@ -20,10 +21,7 @@ def add_arguments(self, parser): ) def handle(self, *args, **options): - # force create event - e, _ = models.Event.objects.get_or_create(name='testit') - e.subscription_set.update_or_create( - event=e, + TEST_EVENT.get_event().subscription_set.update_or_create( defaults={ 'target': options['target'], 'service': models.Subscription.Service.MAIL if options['mail'] else models.Subscription.Service.SLACK, @@ -61,7 +59,7 @@ def handle(self, *args, **options): x = utils.notify(e.name, nblocks) self.stdout.write(f'{x} notifications created') call_command('notification_sender', run_once=True) - return + if options['spam']: x = 0 for y in range(options['spam']): diff --git a/testapp/testapp/models.py b/testapp/testapp/models.py new file mode 100644 index 0000000..4c525bb --- /dev/null +++ b/testapp/testapp/models.py @@ -0,0 +1,3 @@ +from notifications.models import EventRef + +TEST_EVENT = EventRef('testit', 'event for testing purposes (with brute_notify command)')