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

Model field translation overrides default langauge for both fields on save #593

Open
jacobburrell opened this issue Apr 16, 2021 · 8 comments

Comments

@jacobburrell
Copy link

I have a model called 'Status' with one field, 'name'.

I registered the model:

class Status(Model):
    def __str__(self):
        return self.name
    class Meta:
        verbose_name = _('Status')
        verbose_name_plural = _('Statuses')

    name = CharField(verbose_name=_('Status Name'), max_length=100)
@register(Status)
class StatusTranslationOptions(TranslationOptions):
    fields = ('name', )

I registered with and without admin:

class StatusAdmin(TranslationAdmin):
    pass

admin.site.register(Status, StatusAdmin)

I enabled English and Spanish translation and whichever I set my browser configuration to, that's the language that will override the other.

image

I have other models in the same app that are similar that don't override the same way.

@last-partizan
Copy link
Collaborator

Can you explain in steps what's happening?

name_en: English name
name_es: Spanish name

You save object, and what overriding what?

@jacobburrell
Copy link
Author

The input in Status Name [es] will override the contents of Status Name [en] if Spanish is the default.
If English is the default, the [es] field will override the contents of the [en field].

The example of the screenshot upon save will result in both [en] and [es] fields having the value of [en]'s.

Thus after save:
Status Name [en]: English override
Status Name [es]: English override

@last-partizan
Copy link
Collaborator

Now i understand, but have no idea why it happens.

Was it working before last few updates? What versions of django/django-modeltranslation are you using? Are you doing some stuff in signal handlers which may change those fields?

@kathmandu777
Copy link

@last-partizan @jacobburrell
Hi, is there any update on the issue? I have the same problem.
Thank you!

@last-partizan
Copy link
Collaborator

No, there is nothing new.

Please, provide repository with minimal code to reproduce this issue. Try to fix this yourself and make PR.

@kathmandu777
Copy link

@last-partizan
I suspect that the cache is affected by this issue.

Assume name is a field that is translated by django model translation.

queryset = queryset.annotate(
    foo=Case(
        When(Q(name__in=["hoge", "fuga"],  then=True),
        default=Value(False),
        output_field=BooleanField(),
    )
)

I define this and call it at some point. Then, when I try to edit a record in the same model as this one from the Django Admin page, model field translation overrides default langauge for both fields. (Looking directly at the DB, we can confirm that it is stored correctly without being overridden)

So I suspect that when the query is executed, the field value is cached and used as is for display on the django admin page.

Is there a function or option to clear the cache?

Thank you!

@last-partizan
Copy link
Collaborator

I don't remember any caching (but i just maintainer, not author). Better check source code yourself.

@t-mann
Copy link

t-mann commented Jul 10, 2022

The problem is in the cache as it was mentioned by @kathmandu777
But the cache is not a cache like redis or memcache. The problem sits in the django.db.models.fields.Field.get_col that returns self.cached_col that is actually decorated by @cached_property from django.utils.functional.

The fast workaround that works for me was to create custom register decorator with cleaning field cached properties.
I only tested that admin part works correctly thus there still may be some side effects.

Solution allows to fix an issue as quick as possible, specially for projects that are using old versions of the model translations library. I suppose that there is better way to fix this issue in new versions.

"""
Register Decorator Patch
"""

__all__ = [
    'register'
]

# Standard library imports.
import logging

# Related third party imports.
from django.db.models.base import ModelBase

# Local application/library specific imports.


log = logging.getLogger('modeltranslation_patch')


def register(model_or_iterable, raise_exceptions: bool = False, **options):
    """
    Patched decorator to fix issue https://github.com/deschler/django-modeltranslation/issues/593
    Decorator has 1 more parameter `raise_exceptions` that may be used to control exception raising
    in a case if field retrieve from model meta failed
    """
    from modeltranslation.translator import translator, TranslationOptions

    def wrapper(opts_class):
        if not issubclass(opts_class, TranslationOptions):
            raise ValueError('Wrapped class must subclass TranslationOptions.')

        # >>>>>>>>>>> PATCH BEGIN <<<<<<<<<<<<<<
        if isinstance(model_or_iterable, ModelBase):
            models = [model_or_iterable]
        else:
            models = model_or_iterable

        for cls in models:
            for f_name in opts_class.fields:
                try:
                    f_obj = cls._meta.get_field(f_name)
                except Exception as e:
                    if raise_exceptions:
                        raise e
                    log.exception(f'Failed to get field with name `{f_name}` on model {cls.__name__}:\n{e}')
                    continue

                if 'cached_col' in f_obj.__dict__:
                    del f_obj.__dict__['cached_col']

        # >>>>>>>>>>>  PATCH END  <<<<<<<<<<<<<<

        translator.register(models, opts_class, **options)
        return opts_class

    return wrapper

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

No branches or pull requests

4 participants