Make Context Managers (fallbacks and auto_populate) Thread Safe (#657)

This commit is contained in:
Maksudul Haque 2022-11-08 20:13:17 +06:00 committed by GitHub
parent 57b1c33af6
commit 4927b6cb63
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 65 additions and 11 deletions

View file

@ -4,6 +4,7 @@ from django.core.exceptions import ImproperlyConfigured
from django.db.models import fields
from modeltranslation import settings as mt_settings
from modeltranslation.thread_context import fallbacks_enabled
from modeltranslation.utils import (
get_language,
build_localized_fieldname,
@ -350,7 +351,7 @@ class TranslationFieldDescriptor(object):
val = getattr(instance, loc_field_name, None)
if self.meaningful_value(val, undefined):
return val
if mt_settings.ENABLE_FALLBACKS and self.fallback_value is not NONE:
if fallbacks_enabled() and self.fallback_value is not NONE:
return self.fallback_value
else:
if default is NONE:

View file

@ -17,8 +17,8 @@ from django.db.models.query import QuerySet, ValuesIterable
from django.db.models.utils import create_namedtuple_class
from django.utils.tree import Node
from modeltranslation import settings
from modeltranslation.fields import TranslationField
from modeltranslation.thread_context import auto_populate_mode
from modeltranslation.utils import (
auto_populate,
build_localized_fieldname,
@ -376,7 +376,7 @@ class MultilingualQuerySet(QuerySet):
def _populate_mode(self):
# Populate can be set using a global setting or a manager method.
if self._populate is None:
return settings.AUTO_POPULATE
return auto_populate_mode()
return self._populate
# This method was not present in django-linguo

View file

@ -0,0 +1,48 @@
import threading
from typing import Union
from typing_extensions import Literal
from modeltranslation import settings
AutoPopulate = Union[bool, Literal["all", "default", "required"]]
class ModelTranslationThreadLocal(threading.local):
"""Holds thread-local data for modeltranslation."""
auto_populate: Union[AutoPopulate, None] = None
enable_fallbacks: Union[bool, None] = None
_mt_thread_context = ModelTranslationThreadLocal()
def set_auto_populate(value: Union[AutoPopulate, None]) -> None:
"""Set the auto_populate for the current thread."""
_mt_thread_context.auto_populate = value
def set_enable_fallbacks(value: Union[bool, None]) -> None:
"""Set the enable_fallbacks for the current thread."""
_mt_thread_context.enable_fallbacks = value
def auto_populate_mode() -> AutoPopulate:
"""Return the auto_populate mode for the current thread."""
auto_populate = _mt_thread_context.auto_populate
if auto_populate is not None:
return auto_populate
return settings.AUTO_POPULATE
def fallbacks_enabled() -> bool:
"""Return whether fallbacks are enabled for the current thread."""
enable_fallbacks = _mt_thread_context.enable_fallbacks
if enable_fallbacks is not None:
return enable_fallbacks
return settings.ENABLE_FALLBACKS

View file

@ -22,6 +22,7 @@ from modeltranslation.manager import (
rewrite_lookup_key,
append_translated,
)
from modeltranslation.thread_context import auto_populate_mode
from modeltranslation.utils import build_localized_fieldname, parse_field
@ -389,7 +390,7 @@ def populate_translation_fields(sender, kwargs):
defined (for example to make lookups / filtering without resorting to
query fallbacks).
"""
populate = mt_settings.AUTO_POPULATE
populate = auto_populate_mode()
if not populate:
return
if populate is True:

View file

@ -6,6 +6,11 @@ from django.utils.translation import get_language_info
from django.utils.functional import lazy
from modeltranslation import settings
from modeltranslation.thread_context import (
set_auto_populate,
set_enable_fallbacks,
fallbacks_enabled,
)
def get_language():
@ -112,8 +117,9 @@ def resolution_order(lang, override=None):
First is always the parameter language, later are fallback languages.
Override parameter has priority over FALLBACK_LANGUAGES.
"""
if not settings.ENABLE_FALLBACKS:
if not fallbacks_enabled():
return (lang,)
if override is None:
override = {}
fallback_for_lang = override.get(lang, settings.FALLBACK_LANGUAGES.get(lang, ()))
@ -141,12 +147,11 @@ def auto_populate(mode='all'):
with auto_populate('required'):
call_command('loaddata', 'fixture.json')
"""
current_population_mode = settings.AUTO_POPULATE
settings.AUTO_POPULATE = mode
set_auto_populate(mode)
try:
yield
finally:
settings.AUTO_POPULATE = current_population_mode
set_auto_populate(None)
@contextmanager
@ -163,12 +168,11 @@ def fallbacks(enable=True):
processing or check if there is a value for the current language (not
knowing the language)
"""
current_enable_fallbacks = settings.ENABLE_FALLBACKS
settings.ENABLE_FALLBACKS = enable
set_enable_fallbacks(enable)
try:
yield
finally:
settings.ENABLE_FALLBACKS = current_enable_fallbacks
set_enable_fallbacks(None)
def parse_field(setting, field_name, default):