Skip to content

Fix db backup#329

Open
andreweuw wants to merge 3 commits intoopenradx:mainfrom
andreweuw:fix-db-backup
Open

Fix db backup#329
andreweuw wants to merge 3 commits intoopenradx:mainfrom
andreweuw:fix-db-backup

Conversation

@andreweuw
Copy link
Copy Markdown

@andreweuw andreweuw commented Apr 17, 2026

Running dbbackup --clean -v 2 with deprecated settings would result in RuntimeError. After updating the settings, the PostgreSQL backup works again.

Fixes #328.

Summary by CodeRabbit

Release Notes

  • Chores
    • Updated django-dbbackup dependency from version 4.2.1 to 5.3.0 for improved compatibility
    • Modernized backup storage configuration to use Django's unified STORAGES system instead of legacy settings
    • Enhanced static file storage handling in development and production environments

@andreweuw andreweuw requested a review from Copilot April 17, 2026 18:06
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 17, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7106608a-3d5e-4ca1-bd8a-732ff2956e71

📥 Commits

Reviewing files that changed from the base of the PR and between 6a5a97b and 7822248.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • pyproject.toml
✅ Files skipped from review due to trivial changes (1)
  • pyproject.toml

📝 Walkthrough

Walkthrough

The changes migrate deprecated DBBACKUP_STORAGE configuration to Django's unified STORAGES dictionary format and upgrade the django-dbbackup dependency from >=4.2.1 to >=5.3.0. Tests validate successful imports and error detection for legacy settings.

Changes

Cohort / File(s) Summary
Test Suite
adit/core/tests/test_import_dbbackup.py
New test module with helper function and two tests validating dbbackup.settings import behavior—one confirming successful import under default conditions, another verifying that legacy DBBACKUP_STORAGE settings trigger a RuntimeError.
Settings Configuration
adit/settings/base.py, adit/settings/development.py, adit/settings/production.py
Replaced standalone DBBACKUP_STORAGE* variables with unified STORAGES dict in base settings defining default and dbbackup backends. Development settings explicitly set STORAGES["staticfiles"] to StaticFilesStorage; production settings updated to mutate only STORAGES["staticfiles"] while inheriting default from base.
Dependency Update
pyproject.toml
Increased django-dbbackup minimum version constraint from >=4.2.1 to >=5.3.0.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 The storages dance in Django's way,
Old configs fade, new patterns stay,
With STORAGES dict, we hop along,
Dbbackup sings a cleaner song,
Five-point-three brings peace at last! 🎉

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive Title is vague and generic, using non-descriptive phrasing that doesn't convey the specific change of migrating dbbackup settings to Django Storages format. Use a more descriptive title such as 'Migrate dbbackup settings to Django Storages configuration' to clearly indicate the primary change.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Changes align with issue #328 objectives: migrated DBBACKUP_STORAGE configuration to STORAGES dict format, added tests validating the migration, and updated django-dbbackup requirement to >=5.3.0.
Out of Scope Changes check ✅ Passed All changes are scoped to dbbackup configuration migration and related tests; no unrelated modifications detected outside the defined objectives.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates Django’s storage configuration to the new STORAGES-based setup required by django-dbbackup>=5, preventing runtime import failures caused by deprecated DBBACKUP_STORAGE* settings (Fixes #328).

Changes:

  • Migrate dbbackup storage configuration from deprecated DBBACKUP_STORAGE / DBBACKUP_STORAGE_OPTIONS to STORAGES["dbbackup"].
  • Refactor production.py / development.py to override only STORAGES["staticfiles"] rather than redefining the full STORAGES dict.
  • Add regression tests ensuring dbbackup.settings imports successfully with the new configuration and fails when legacy settings are present.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
adit/settings/base.py Introduces STORAGES with default and dbbackup storage backends to replace deprecated dbbackup settings.
adit/settings/production.py Switches to overriding only the staticfiles storage backend (whitenoise).
adit/settings/development.py Adds an explicit staticfiles storage backend for development.
adit/core/tests/test_import_dbbackup.py Adds tests to catch the deprecated-settings import RuntimeError regression.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread adit/settings/base.py
Comment on lines +339 to 348
# django default storage and django-dbbackup
STORAGES = {
"default": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
},
"dbbackup": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
"OPTIONS": {"location": env.str("DBBACKUP_STORAGE_LOCATION", default="/tmp/backups-adit")},
},
}
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

STORAGES is now explicitly set in base.py but it does not include a "staticfiles" backend. In environments that only import base (notably adit/settings/test.py), Django’s staticfiles system may raise InvalidStorageError / fail to resolve storages["staticfiles"]. Consider defining a sensible default "staticfiles" entry in base.py (e.g., django.contrib.staticfiles.storage.StaticFilesStorage) and overriding only the backend in development.py/production.py.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

From my point of view, not defining "staticfiles" STORAGES entry while adding new settings outside the bounds of development.py/production.py should not lead to fault-tolerant behavior. In case that happens, Django produces the following error if one tries to compose-up without the setting:

django.core.files.storage.handler.InvalidStorageError: Could not find config for 'staticfiles' in settings.STORAGES.

Comment thread adit/settings/base.py
Comment on lines +339 to +347
# django default storage and django-dbbackup
STORAGES = {
"default": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
},
"dbbackup": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
"OPTIONS": {"location": env.str("DBBACKUP_STORAGE_LOCATION", default="/tmp/backups-adit")},
},
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

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

The PR description/issue notes mention raising the required django-dbbackup version to >=5.0.0, but the changes here switch configuration to STORAGES["dbbackup"] (the new-style config). If the dependency constraint isn’t also updated, environments that resolve django-dbbackup<5 may not recognize this setting and backups could break. Please ensure the dependency/version pin is updated consistently with this new configuration.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fair enough

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request migrates the project to the Django 5.1 STORAGES setting, replacing the deprecated DBBACKUP_STORAGE and DBBACKUP_STORAGE_OPTIONS configurations. The changes centralize storage definitions in base.py and update development.py and production.py to modify the STORAGES dictionary instead of overwriting it. A new test file was added to verify that legacy settings correctly trigger a RuntimeError. Feedback was provided regarding the use of the private _storages attribute in tests, which is considered fragile and could cause side effects in parallel test execution.



def _import_dbbackup_settings():
storages._storages.clear() # type: ignore[attr-defined]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Accessing and clearing the private _storages attribute of the Django StorageHandler is a fragile approach for a test. While it might be necessary here to force a re-evaluation of settings during the dbbackup import, it can lead to unexpected side effects in other tests that rely on the storage cache, especially if tests are run in parallel. Consider if there is a more public way to reset the storage state or if the test can be isolated further.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
adit/core/tests/test_import_dbbackup.py (2)

12-15: Leaks global state; consider a cleanup fixture to restore sys.modules and storages._storages.

_import_dbbackup_settings clears the storages registry and drops dbbackup.settings from sys.modules before re‑importing, but nothing restores prior state after each test. Any subsequent test that touches the storages registry (e.g., default_storage, media uploads, or other code relying on a cached dbbackup.settings) can observe a cleared/re-evaluated state depending on collection order, which makes the suite order-dependent and hard to diagnose later.

Additionally, storages._storages is a private attribute of django.core.files.storage.InMemoryStorageHandler; the type: ignore[attr-defined] hint already signals this. That's fine for a targeted test, but worth isolating behind a fixture so the coupling is in one place.

♻️ Proposed fixture-based isolation
 import importlib
 import sys

 import pytest
 from django.core.files.storage import storages

 DEPRECATED_DBBACKUP_SETTINGS_ERROR = (
     "The settings DBBACKUP_STORAGE and DBBACKUP_STORAGE_OPTIONS have been deprecated"
 )


+@pytest.fixture
+def reimport_dbbackup_settings():
+    """Restore storages registry and dbbackup.settings module after the test."""
+    saved_storages = dict(storages._storages)  # type: ignore[attr-defined]
+    saved_module = sys.modules.get("dbbackup.settings")
+    try:
+        yield _import_dbbackup_settings
+    finally:
+        storages._storages.clear()  # type: ignore[attr-defined]
+        storages._storages.update(saved_storages)  # type: ignore[attr-defined]
+        if saved_module is not None:
+            sys.modules["dbbackup.settings"] = saved_module
+        else:
+            sys.modules.pop("dbbackup.settings", None)
+
+
 def _import_dbbackup_settings():
     storages._storages.clear()  # type: ignore[attr-defined]
     sys.modules.pop("dbbackup.settings", None)
     importlib.import_module("dbbackup.settings")


-def test_dbbackup_settings_import_succeeds():
-    _import_dbbackup_settings()
+def test_dbbackup_settings_import_succeeds(reimport_dbbackup_settings):
+    reimport_dbbackup_settings()


-def test_dbbackup_settings_import_fails_with_legacy_storage_settings(settings):
+def test_dbbackup_settings_import_fails_with_legacy_storage_settings(
+    settings, reimport_dbbackup_settings
+):
     settings.DBBACKUP_STORAGE = "irrelevant"
     settings.DBBACKUP_STORAGE_OPTIONS = {}

     with pytest.raises(
         RuntimeError,
         match=DEPRECATED_DBBACKUP_SETTINGS_ERROR,
     ):
-        _import_dbbackup_settings()
+        reimport_dbbackup_settings()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@adit/core/tests/test_import_dbbackup.py` around lines 12 - 15, The helper
_import_dbbackup_settings mutates global state by clearing storages._storages
and removing "dbbackup.settings" from sys.modules without restoring them,
causing order-dependent test failures; change this by moving the behavior into a
pytest fixture that (1) snapshots storages._storages (and the existence/value of
sys.modules["dbbackup.settings"]) before clearing, (2) performs the import
manipulation (clear storages._storages, pop "dbbackup.settings",
importlib.import_module("dbbackup.settings")) for the test, and (3) on teardown
restores the original storages._storages content and restores
sys.modules["dbbackup.settings"] (or removes it if it did not exist),
referencing the existing helper name _import_dbbackup_settings and the symbols
storages._storages and sys.modules["dbbackup.settings"] so callers can switch to
the fixture without leaking global state.

7-9: Consider pinning django-dbbackup version or loosening the error message expectation for robustness.

The test uses regex matching on the constant "The settings DBBACKUP_STORAGE and DBBACKUP_STORAGE_OPTIONS have been deprecated", which is a prefix of the actual deprecation message. While this currently works, the dependency in pyproject.toml is unpinned (>=4.2.1), allowing any future version. If upstream changes the deprecation message wording, the test will fail silently until the exception is raised. Either pin the version constraint to stabilize the expected message or loosen the regex match to check only for key terms like "DBBACKUP_STORAGE" and "deprecated".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@adit/core/tests/test_import_dbbackup.py` around lines 7 - 9, The test's
expectation uses the full DEPRECATED_DBBACKUP_SETTINGS_ERROR constant which is
fragile given an unpinned django-dbbackup dependency; update the test in
test_import_dbbackup.py to loosen the assertion by checking that the raised
deprecation message contains key terms (e.g., "DBBACKUP_STORAGE" and
"deprecated") instead of matching the entire DEPRECATED_DBBACKUP_SETTINGS_ERROR
string, or alternatively pin the django-dbbackup version in pyproject.toml to a
fixed release (e.g., ==4.2.1) to stabilize the exact message; reference
DEPRECATED_DBBACKUP_SETTINGS_ERROR in the test when implementing the containment
checks.
adit/settings/base.py (1)

339-348: Consider including the default "staticfiles" entry in base STORAGES.

Django replaces (not merges) the STORAGES default when the setting is defined, so this base mapping silently drops the built‑in "staticfiles" entry. It works today only because both adit/settings/development.py and adit/settings/production.py explicitly re‑add STORAGES["staticfiles"]. Any future settings module that forgets to do the same (e.g., a new test.py, a management-script settings, CI helper) will fail to collect static files or raise InvalidStorageError. Keeping the default here makes the base self-sufficient and removes the duplication between the two environment files.

♻️ Proposed fix
 # django default storage and django-dbbackup
 STORAGES = {
     "default": {
         "BACKEND": "django.core.files.storage.FileSystemStorage",
     },
+    "staticfiles": {
+        "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
+    },
     "dbbackup": {
         "BACKEND": "django.core.files.storage.FileSystemStorage",
         "OPTIONS": {"location": env.str("DBBACKUP_STORAGE_LOCATION", default="/tmp/backups-adit")},
     },
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@adit/settings/base.py` around lines 339 - 348, The STORAGES dict in base
settings currently omits the Django default "staticfiles" entry, which causes
replacement of the default mapping; add a "staticfiles" key to the STORAGES
mapping in adit/settings/base.py (alongside "default" and "dbbackup") using the
Django default backend (e.g., backend
"django.contrib.staticfiles.storage.StaticFilesStorage") so the base settings
are self-sufficient and other environment-specific settings no longer need to
re-add STORAGES["staticfiles"].
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@adit/settings/base.py`:
- Around line 339-349: The project pins django-dbbackup >=4.2.1 but the settings
use the STORAGES["dbbackup"] key (and related DBBACKUP_CLEANUP_KEEP behavior)
which requires django-dbbackup 5.x; update the dependency floor in
pyproject.toml from >=4.2.1 to >=5.0.0 so installs pull a compatible
django-dbbackup that understands the "dbbackup" entry under STORAGES and avoids
using the removed legacy DBBACKUP_STORAGE* keys.

---

Nitpick comments:
In `@adit/core/tests/test_import_dbbackup.py`:
- Around line 12-15: The helper _import_dbbackup_settings mutates global state
by clearing storages._storages and removing "dbbackup.settings" from sys.modules
without restoring them, causing order-dependent test failures; change this by
moving the behavior into a pytest fixture that (1) snapshots storages._storages
(and the existence/value of sys.modules["dbbackup.settings"]) before clearing,
(2) performs the import manipulation (clear storages._storages, pop
"dbbackup.settings", importlib.import_module("dbbackup.settings")) for the test,
and (3) on teardown restores the original storages._storages content and
restores sys.modules["dbbackup.settings"] (or removes it if it did not exist),
referencing the existing helper name _import_dbbackup_settings and the symbols
storages._storages and sys.modules["dbbackup.settings"] so callers can switch to
the fixture without leaking global state.
- Around line 7-9: The test's expectation uses the full
DEPRECATED_DBBACKUP_SETTINGS_ERROR constant which is fragile given an unpinned
django-dbbackup dependency; update the test in test_import_dbbackup.py to loosen
the assertion by checking that the raised deprecation message contains key terms
(e.g., "DBBACKUP_STORAGE" and "deprecated") instead of matching the entire
DEPRECATED_DBBACKUP_SETTINGS_ERROR string, or alternatively pin the
django-dbbackup version in pyproject.toml to a fixed release (e.g., ==4.2.1) to
stabilize the exact message; reference DEPRECATED_DBBACKUP_SETTINGS_ERROR in the
test when implementing the containment checks.

In `@adit/settings/base.py`:
- Around line 339-348: The STORAGES dict in base settings currently omits the
Django default "staticfiles" entry, which causes replacement of the default
mapping; add a "staticfiles" key to the STORAGES mapping in
adit/settings/base.py (alongside "default" and "dbbackup") using the Django
default backend (e.g., backend
"django.contrib.staticfiles.storage.StaticFilesStorage") so the base settings
are self-sufficient and other environment-specific settings no longer need to
re-add STORAGES["staticfiles"].
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 21a0e8d7-dc4b-44dc-8cae-04f7d6f652ec

📥 Commits

Reviewing files that changed from the base of the PR and between 62f91dd and 6a5a97b.

📒 Files selected for processing (4)
  • adit/core/tests/test_import_dbbackup.py
  • adit/settings/base.py
  • adit/settings/development.py
  • adit/settings/production.py

Comment thread adit/settings/base.py
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 6 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@andreweuw andreweuw marked this pull request as ready for review April 17, 2026 20:51
@andreweuw andreweuw added bug Something isn't working priority-high labels Apr 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working priority-high

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update dbbackup settings

2 participants