Skip to content

Dj celery panel#8 db based periodic tasks - #13

Merged
yassi merged 3 commits into
mainfrom
dj-celery-panel#8_db_based_periodic_tasks
Jan 29, 2026
Merged

Dj celery panel#8 db based periodic tasks#13
yassi merged 3 commits into
mainfrom
dj-celery-panel#8_db_based_periodic_tasks

Conversation

@yassi

@yassi yassi commented Jan 29, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for multiple periodic tasks backends—users can now retrieve periodic tasks from Celery configuration or django-celery-beat database
    • Added backend information display in the UI showing which periodic tasks source is currently active
    • Added error handling with user notifications for periodic tasks retrieval failures
  • Documentation

    • Updated configuration documentation with periodic tasks backend setup guide and examples

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Jan 29, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The PR introduces a pluggable backend system for retrieving Celery periodic tasks from different sources, including beat_schedule configuration and django-celery-beat database. It replaces direct inspector calls with an interface-based approach, adds configurable backends, includes comprehensive test coverage, and updates documentation with configuration examples.

Changes

Cohort / File(s) Summary
Periodic Tasks Core Module
dj_celery_panel/celery_utils/periodic_tasks.py
Introduces new pluggable periodic tasks retrieval with PeriodicTaskListPage dataclass, CeleryPeriodicTasksInterface for backend delegation, CeleryPeriodicTasksConfigBackend for beat_schedule parsing, and CeleryPeriodicTasksDjangoCeleryBeatBackend for django-celery-beat database integration with robust error handling.
Public API Exports
dj_celery_panel/celery_utils/__init__.py
Extends public API surface with imports and \_\all\_\ exports for four new periodic tasks components: CeleryPeriodicTasksConfigBackend, CeleryPeriodicTasksDjangoCeleryBeatBackend, CeleryPeriodicTasksInterface, and PeriodicTaskListPage.
View and Template Layer
dj_celery_panel/views.py, dj_celery_panel/templates/admin/dj_celery_panel/index.html
Replaces direct inspector.get_periodic_tasks() calls with CeleryPeriodicTasksInterface abstraction, adds error message handling, retrieves backend metadata for display, and includes backend info template partial in periodic tasks section.
Documentation and Configuration
docs/configuration.md, README.md
Adds documentation for periodic tasks backend configuration with examples for both ConfigBackend and DjangoCeleryBeatBackend, includes custom backend implementation guide, and notes backend swappability via DJ_CELERY_PANEL_SETTINGS.
Project Setup
example_project/example_project/settings.py, requirements.txt
Adds django-celery-beat>=2.8.1 dependency and installs django_celery_beat app in example project with commented configuration options for periodic tasks backend selection.
Test Suite
tests/test_periodic_tasks.py, tests/test_periodic_tasks_integration.py
Comprehensive unit tests for backend implementations covering beat_schedule and django-celery-beat database retrieval, error handling, and metadata attributes; integration tests verify index view rendering with different backend configurations and backend info display.

Sequence Diagram

sequenceDiagram
    participant View as View Layer
    participant Interface as CeleryPeriodicTasksInterface
    participant Backend as Configured Backend
    participant Source as Data Source

    View->>Interface: get_periodic_tasks()
    Interface->>Interface: Load configured backend<br/>(or DEFAULT_BACKEND)
    Interface->>Backend: Instantiate with app
    Backend->>Source: Fetch periodic tasks
    Source-->>Backend: Return task data
    Backend->>Backend: Parse & format tasks<br/>to PeriodicTaskListPage
    Backend-->>Interface: PeriodicTaskListPage
    Interface-->>View: PeriodicTaskListPage<br/>(with tasks & error)
    View->>View: Display tasks & handle error<br/>if present
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly Related PRs

  • Dj celery panel#2 task inspect backend #3: Introduces backend-info UI infrastructure and CeleryAbstractInterface.get_backend_info() method that the periodic tasks feature leverages for displaying backend metadata in templates.

Poem

🐰 A new backend system hops into view,
Periodic tasks dance in configurations too,
Config or database, take your pick,
This pluggable magic is ever so slick!
Schedules hop by, from source to display, ⏰✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title references issue #8 and mentions 'db based periodic tasks', which aligns with the PR's core objective of adding database-based periodic task backends and improving periodic tasks support.
Docstring Coverage ✅ Passed Docstring coverage is 93.10% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Jan 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.00000% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.29%. Comparing base (eee1c9b) to head (a8df981).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
dj_celery_panel/celery_utils/periodic_tasks.py 81.25% 10 Missing and 2 partials ⚠️
dj_celery_panel/views.py 60.00% 1 Missing and 1 partial ⚠️

❌ Your project status has failed because the head coverage (76.29%) is below the target coverage (80.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main      #13      +/-   ##
==========================================
+ Coverage   76.23%   76.29%   +0.05%     
==========================================
  Files          12       13       +1     
  Lines         606      675      +69     
  Branches       74       82       +8     
==========================================
+ Hits          462      515      +53     
- Misses        112      129      +17     
+ Partials       32       31       -1     
Flag Coverage Δ
django-4.2 76.29% <80.00%> (+0.05%) ⬆️
django-5.2 75.75% <79.41%> (+0.04%) ⬆️
django-6 75.75% <79.41%> (+0.04%) ⬆️
python-3.14 75.75% <79.41%> (+0.04%) ⬆️
python-3.9 76.29% <80.00%> (+0.05%) ⬆️
unittests 76.29% <80.00%> (+0.05%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
dj_celery_panel/celery_utils/__init__.py 100.00% <100.00%> (ø)
dj_celery_panel/views.py 91.58% <60.00%> (-1.62%) ⬇️
dj_celery_panel/celery_utils/periodic_tasks.py 81.25% <81.25%> (ø)

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@dj_celery_panel/celery_utils/periodic_tasks.py`:
- Around line 95-97: The select_related call on
PeriodicTask.objects.filter(enabled=True).select_related("interval", "crontab",
"solar") omits "clocked", causing an N+1 query when the code later accesses
task.clocked; update the select_related invocation to include "clocked" so the
clocked relation is fetched in the same query (locate the select_related call in
periodic_tasks.py and add "clocked" to the tuple/list of relations).

In `@tests/test_periodic_tasks_integration.py`:
- Around line 16-28: The Celery instance created in setUp (self.test_app) is
never wired to the view's global current_app; update setUp to replace the
module-level Celery current_app used by the view with self.test_app (and restore
it in tearDown) so the view reads the test beat_schedule; specifically, patch or
assign celery.current_app (or the view module's reference to current_app) to
self.test_app inside setUp and revert the change in tearDown so tests actually
exercise the periodic tasks in the view.
🧹 Nitpick comments (4)
tests/test_periodic_tasks.py (1)

125-144: Consider using underscore prefix for intentionally unused variables.

The task1 and task2 variables are assigned but never directly used in the test assertions (the test queries them back from the database). While this is intentional, using underscore prefix makes the intent clearer and silences linter warnings.

♻️ Suggested fix
         # Create a periodic task with interval schedule
-        task1 = PeriodicTask.objects.create(
+        PeriodicTask.objects.create(
             name="db-task-1",
             task="app.tasks.db_task",
             interval=self.interval_schedule,
             enabled=True,
             args='[1, 2]',
             kwargs='{"key": "value"}',
             total_run_count=5,
         )
         
         # Create a periodic task with crontab schedule
-        task2 = PeriodicTask.objects.create(
+        PeriodicTask.objects.create(
             name="db-task-2",
             task="app.tasks.another_db_task",
             crontab=self.crontab_schedule,
             enabled=True,
             args="[]",
             kwargs="{}",
             total_run_count=10,
         )
dj_celery_panel/celery_utils/periodic_tasks.py (3)

62-63: Consider narrowing the exception scope and using f-string conversion.

While catching broad exceptions provides robustness, it can mask unexpected errors. Additionally, using {e!s} is more idiomatic than str(e).

♻️ Suggested improvement
-        except Exception as e:
-            error = f"Error reading beat_schedule: {str(e)}"
+        except Exception as e:
+            error = f"Error reading beat_schedule: {e!s}"

109-120: Move import json outside the loop.

The json import is placed inside the loop, causing unnecessary repeated module lookups. Move it to the top of the method or the module.

♻️ Suggested fix
+        import json
+
         try:
             from django_celery_beat.models import PeriodicTask

             # Query all enabled periodic tasks
             for task in PeriodicTask.objects.filter(enabled=True).select_related(
                 "interval", "crontab", "solar", "clocked"
             ):
                 # Determine the schedule string based on which schedule type is set
                 schedule_str = "N/A"
                 if task.interval:
                     schedule_str = str(task.interval)
                 elif task.crontab:
                     schedule_str = str(task.crontab)
                 elif task.solar:
                     schedule_str = str(task.solar)
                 elif task.clocked:
                     schedule_str = str(task.clocked)

                 # Parse args and kwargs (stored as JSON strings)
-                import json
-
                 try:
                     args = json.loads(task.args) if task.args else []

137-138: Consider using f-string conversion flag.

Using {e!s} is more idiomatic than str(e) in f-strings.

♻️ Suggested fix
         except Exception as e:
-            error = f"Error reading periodic tasks from database: {str(e)}"
+            error = f"Error reading periodic tasks from database: {e!s}"

Comment on lines +95 to +97
for task in PeriodicTask.objects.filter(enabled=True).select_related(
"interval", "crontab", "solar"
):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing clocked in select_related causes N+1 query.

The code checks for task.clocked at Line 106, but clocked is not included in select_related. This will trigger an additional database query for each task that uses a clocked schedule.

🔧 Proposed fix
             for task in PeriodicTask.objects.filter(enabled=True).select_related(
-                "interval", "crontab", "solar"
+                "interval", "crontab", "solar", "clocked"
             ):
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for task in PeriodicTask.objects.filter(enabled=True).select_related(
"interval", "crontab", "solar"
):
for task in PeriodicTask.objects.filter(enabled=True).select_related(
"interval", "crontab", "solar", "clocked"
):
🤖 Prompt for AI Agents
In `@dj_celery_panel/celery_utils/periodic_tasks.py` around lines 95 - 97, The
select_related call on
PeriodicTask.objects.filter(enabled=True).select_related("interval", "crontab",
"solar") omits "clocked", causing an N+1 query when the code later accesses
task.clocked; update the select_related invocation to include "clocked" so the
clocked relation is fetched in the same query (locate the select_related call in
periodic_tasks.py and add "clocked" to the tuple/list of relations).

Comment on lines +16 to +28
def setUp(self):
"""Set up test fixtures."""
super().setUp()
# Create a test Celery app with beat schedule
self.test_app = Celery("test_app")
self.test_app.conf.beat_schedule = {
"test-periodic-task": {
"task": "app.tasks.test_periodic",
"schedule": crontab(minute="*/5"),
"args": [1, 2],
"kwargs": {"key": "value"},
},
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check how the views retrieve the Celery app and periodic tasks
rg -n "CeleryPeriodicTasksInterface" --type py -A 3 dj_celery_panel/views.py

# Check the base test case to see if it sets up the Celery app
rg -n "class CeleryPanelTestCase" --type py -A 20 tests/base.py

Repository: yassi/dj-celery-panel

Length of output: 1041


🏁 Script executed:

# Check imports and context of current_app in views.py
rg -n "from.*current_app|import.*current_app" --type py dj_celery_panel/views.py

# Look at the full test file to see what tests are defined
head -80 tests/test_periodic_tasks_integration.py

# Check if there's any monkey patching or app configuration in the test file
rg -n "current_app|patch|mock" --type py tests/test_periodic_tasks_integration.py

Repository: yassi/dj-celery-panel

Length of output: 3305


🏁 Script executed:

# Check if test_app is referenced anywhere else in the test file
rg -n "test_app\|current_app" --type py tests/test_periodic_tasks_integration.py

# Check for any patching or Django app configuration that might connect them
rg -n "patch\|mock\|CELERY\|current_app" --type py tests/

# Check the full views.py context around the periodic tasks call
sed -n '1,40p' dj_celery_panel/views.py

Repository: yassi/dj-celery-panel

Length of output: 1373


Verify that test_app is connected to the view being tested.

The test_app Celery instance is created with a beat_schedule in setUp(), but it is never used by any of the test methods. The view uses the global current_app from the Celery package (line 30 in views.py), which is a completely separate instance. The tests only verify the presence of text strings like "Periodic Tasks" and "Backend:", not whether the periodic tasks from test_app are actually displayed. This means the tests may pass for the wrong reasons and don't validate that periodic tasks are properly integrated with the view.

🤖 Prompt for AI Agents
In `@tests/test_periodic_tasks_integration.py` around lines 16 - 28, The Celery
instance created in setUp (self.test_app) is never wired to the view's global
current_app; update setUp to replace the module-level Celery current_app used by
the view with self.test_app (and restore it in tearDown) so the view reads the
test beat_schedule; specifically, patch or assign celery.current_app (or the
view module's reference to current_app) to self.test_app inside setUp and revert
the change in tearDown so tests actually exercise the periodic tasks in the
view.

@yassi
yassi merged commit de15287 into main Jan 29, 2026
5 of 6 checks passed
@yassi yassi linked an issue Jan 29, 2026 that may be closed by this pull request
@coderabbitai coderabbitai Bot mentioned this pull request Aug 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Get scheduled task via django-celery-beat

1 participant