Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed signals can not connect to OneToOneField (#572) #573

Merged
merged 9 commits into from
Oct 13, 2022
4 changes: 4 additions & 0 deletions django_celery_beat/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@ class BeatConfig(AppConfig):
label = 'django_celery_beat'
verbose_name = _('Periodic Tasks')
default_auto_field = 'django.db.models.AutoField'

def ready(self):
from .signals import signals_connect
signals_connect()
26 changes: 5 additions & 21 deletions django_celery_beat/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from django.core.exceptions import MultipleObjectsReturned, ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.db.models import signals
from django.utils.translation import gettext_lazy as _

from . import managers, validators
Expand Down Expand Up @@ -586,6 +585,11 @@ def save(self, *args, **kwargs):
self._clean_expires()
self.validate_unique()
super().save(*args, **kwargs)
PeriodicTasks.changed(self)

def delete(self, *args, **kwargs):
super().delete(*args, **kwargs)
PeriodicTasks.changed(self)

def _clean_expires(self):
if self.expire_seconds is not None and self.expires:
Expand Down Expand Up @@ -619,23 +623,3 @@ def schedule(self):
return self.solar.schedule
if self.clocked:
return self.clocked.schedule


signals.pre_delete.connect(PeriodicTasks.changed, sender=PeriodicTask)
signals.pre_save.connect(PeriodicTasks.changed, sender=PeriodicTask)
signals.pre_delete.connect(
PeriodicTasks.update_changed, sender=IntervalSchedule)
signals.post_save.connect(
PeriodicTasks.update_changed, sender=IntervalSchedule)
signals.post_delete.connect(
PeriodicTasks.update_changed, sender=CrontabSchedule)
signals.post_save.connect(
PeriodicTasks.update_changed, sender=CrontabSchedule)
signals.post_delete.connect(
PeriodicTasks.update_changed, sender=SolarSchedule)
signals.post_save.connect(
PeriodicTasks.update_changed, sender=SolarSchedule)
signals.post_delete.connect(
PeriodicTasks.update_changed, sender=ClockedSchedule)
signals.post_save.connect(
PeriodicTasks.update_changed, sender=ClockedSchedule)
48 changes: 48 additions & 0 deletions django_celery_beat/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
def signals_connect():
"""
Connect to signals.
"""
from django.db.models import signals
from .models import (
ClockedSchedule,
PeriodicTask,
PeriodicTasks,
IntervalSchedule,
CrontabSchedule,
SolarSchedule
)

signals.pre_save.connect(
PeriodicTasks.changed, sender=PeriodicTask
)
signals.pre_delete.connect(
PeriodicTasks.changed, sender=PeriodicTask
)

signals.post_save.connect(
PeriodicTasks.update_changed, sender=IntervalSchedule
)
signals.pre_delete.connect(
PeriodicTasks.update_changed, sender=IntervalSchedule
)

signals.post_save.connect(
PeriodicTasks.update_changed, sender=CrontabSchedule
)
signals.post_delete.connect(
PeriodicTasks.update_changed, sender=CrontabSchedule
)

signals.post_save.connect(
PeriodicTasks.update_changed, sender=SolarSchedule
)
signals.post_delete.connect(
PeriodicTasks.update_changed, sender=SolarSchedule
)

signals.post_save.connect(
PeriodicTasks.update_changed, sender=ClockedSchedule
)
signals.post_delete.connect(
PeriodicTasks.update_changed, sender=ClockedSchedule
)
23 changes: 23 additions & 0 deletions t/proj/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 4.0.7 on 2022-10-13 05:09

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
('django_celery_beat', '0016_alter_crontabschedule_timezone'),
]

operations = [
migrations.CreateModel(
name='O2OToPeriodicTasks',
fields=[
('periodictask_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='django_celery_beat.periodictask')),
],
bases=('django_celery_beat.periodictask',),
),
]
Empty file added t/proj/migrations/__init__.py
Empty file.
8 changes: 8 additions & 0 deletions t/proj/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django_celery_beat.models import PeriodicTask


class O2OToPeriodicTasks(PeriodicTask):
"""
The test-case model of OneToOne relation.
"""
pass
5 changes: 1 addition & 4 deletions t/proj/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
'django.contrib.messages',
'django.contrib.staticfiles',
'django_celery_beat',
't.proj',
]

MIDDLEWARE = [
Expand Down Expand Up @@ -79,7 +80,6 @@

WSGI_APPLICATION = 't.proj.wsgi.application'


# Database
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases

Expand All @@ -93,7 +93,6 @@
}
}


# Password validation
# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators

Expand All @@ -102,7 +101,6 @@
AUTH_PASSWORD_VALIDATORS = [
]


# Internationalization
# https://docs.djangoproject.com/en/1.9/topics/i18n/

Expand All @@ -116,7 +114,6 @@

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.9/howto/static-files/

Expand Down
54 changes: 54 additions & 0 deletions t/unit/test_models.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import datetime
import os

try:
from zoneinfo import available_timezones, ZoneInfo
except ImportError:
from backports.zoneinfo import available_timezones, ZoneInfo

import pytest

from celery import schedules
from django.test import TestCase, override_settings
from django.apps import apps
Expand All @@ -22,7 +25,10 @@
CrontabSchedule,
ClockedSchedule,
IntervalSchedule,
PeriodicTasks,
DAYS,
)
from t.proj.models import O2OToPeriodicTasks


class MigrationTests(TestCase):
Expand Down Expand Up @@ -159,3 +165,51 @@ def test_timezone_format(self):
clocked_time=tz_info)
# testnig str(schedule) calls make_aware() internally
assert str(schedule.clocked_time) == str(schedule)


@pytest.mark.django_db()
class OneToOneRelTestCase(TestCase):
"""
Make sure that when OneToOne relation Model changed,
the `PeriodicTasks.last_update` will be update.
"""

@classmethod
def setUpTestData(cls):
super().setUpTestData()
cls.interval_schedule = IntervalSchedule.objects.create(
every=10, period=DAYS
)

def test_trigger_update_when_saved(self):
o2o_to_periodic_tasks = O2OToPeriodicTasks.objects.create(
name='name1',
task='task1',
enabled=True,
interval=self.interval_schedule
)
not_changed_dt = PeriodicTasks.last_change()
o2o_to_periodic_tasks.enabled = True # Change something on instance.
o2o_to_periodic_tasks.save()
has_changed_dt = PeriodicTasks.last_change()
self.assertTrue(
not_changed_dt != has_changed_dt,
'The `PeriodicTasks.last_update` has not be update.'
)
# Check the `PeriodicTasks` does be updated.

def test_trigger_update_when_deleted(self):
o2o_to_periodic_tasks = O2OToPeriodicTasks.objects.create(
name='name1',
task='task1',
enabled=True,
interval=self.interval_schedule
)
not_changed_dt = PeriodicTasks.last_change()
o2o_to_periodic_tasks.delete()
has_changed_dt = PeriodicTasks.last_change()
self.assertTrue(
not_changed_dt != has_changed_dt,
'The `PeriodicTasks.last_update` has not be update.'
)
# Check the `PeriodicTasks` does be updated.