Skip to content

Added async support#656

Open
PhilippTh wants to merge 2 commits intojazzband:masterfrom
PhilippTh:async-support
Open

Added async support#656
PhilippTh wants to merge 2 commits intojazzband:masterfrom
PhilippTh:async-support

Conversation

@PhilippTh
Copy link

@PhilippTh PhilippTh commented Jan 27, 2026

Summary

This PR implements comprehensive asynchronous support for django-constance, making configuration access seamless and efficient in both sync and async Django environments.

Why?

Previously, using Constance in an async context (like a Django async def view) was problematic:

  1. Thread Blocking: Accessing config.KEY synchronously inside an async view blocks the event loop, negating the performance benefits of async.
  2. Database Errors: For the Database backend, Django's safety guards would raise a SynchronousOnlyOperation error because database queries are prohibited on the main async thread.
  3. Manual Overhead: Developers had to manually wrap every Constance access in await sync_to_async(lambda: config.KEY)(), which is verbose and prone to errors.

This implementation removes these barriers by making the config object "async-agnostic"—it detects the execution context and provides the correct, non-blocking path automatically.

How to Use

  1. Simple Async Access

In an async view or function, you can now simply await any configuration key:

async def my_view(request):
    # Non-blocking async retrieval
    if await config.MY_SETTING:
        ...
  1. Async Updates and Bulk Retrieval

New native async methods have been added for updates and batch operations:

# Update a setting asynchronously
await config.aset('MY_SETTING', 'new_value')
# Fetch multiple settings in one non-blocking call
values = await config.amget(['SETTING_A', 'SETTING_B'])
  1. Gradual Migration & Safety Guards

If you forget to use await inside an async loop, the app will still work (to prevent crashes), but it will:

  • Emit a Warning: A RuntimeWarning will be triggered: "Synchronous access to Constance setting 'KEY' inside an async loop. Use 'await config.KEY' instead."
  • Database Safety: If using the Database backend, it will still raise SynchronousOnlyOperation if accessed synchronously without await, nothing changed here.
  • Any previous implementations that use await sync_to_async(lambda: config.KEY)() will continue to work without any issues or drawbacks. Dropping the sync_to_async-lambda combination will increase performance or at the very least developer experience though.

Key Technical Changes

  • AsyncValueProxy: A lazy-loading proxy that implements await while supporting standard Python operators (==, +, bool(), etc.) for backward compatibility.
  • Native Backend Support:
    • DatabaseBackend now uses afirst() and afetch() for non-blocking I/O.
    • RedisBackend leverages redis.asyncio for native speed.
  • Context Detection: Uses asyncio.get_running_loop() to switch behavior at runtime without performance penalty in synchronous code.

Verification

  • Added tests/test_async.py covering all new async paths.
  • All existing synchronous tests pass.

@codecov
Copy link

codecov bot commented Jan 30, 2026

Codecov Report

❌ Patch coverage is 86.08696% with 32 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.20%. Comparing base (d2b8ca1) to head (5a9f416).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
constance/backends/redisd.py 75.94% 12 Missing and 7 partials ⚠️
constance/base.py 93.10% 3 Missing and 3 partials ⚠️
constance/backends/database.py 88.88% 3 Missing and 1 partial ⚠️
constance/backends/__init__.py 50.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #656      +/-   ##
==========================================
- Coverage   88.28%   88.20%   -0.08%     
==========================================
  Files          21       21              
  Lines         751      975     +224     
  Branches      123      167      +44     
==========================================
+ Hits          663      860     +197     
- Misses         62       80      +18     
- Partials       26       35       +9     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

1 participant