def add_translation_fields(model, opts):
    """
    Monkey patches the original model class to provide additional fields for
    every language.

    Adds newly created translation fields to the given translation options.
    """
    model_empty_values = getattr(opts, 'empty_values', NONE)
    for field_name in opts.local_fields.keys():
        field_empty_value = parse_field(model_empty_values, field_name, NONE)
        for l in mt_settings.AVAILABLE_LANGUAGES:
            # Create a dynamic translation field
            translation_field = create_translation_field(
                model=model, field_name=field_name, lang=l, empty_value=field_empty_value)
            # Construct the name for the localized field
            localized_field_name = build_localized_fieldname(field_name, l)
            # Check if the model already has a field by that name
            if hasattr(model, localized_field_name):
                raise ValueError(
                    "Error adding translation field. Model '%s' already contains a field named"
                    "'%s'." % (model._meta.object_name, localized_field_name))
            # This approach implements the translation fields as full valid
            # django model fields and therefore adds them via add_to_class
            model.add_to_class(localized_field_name, translation_field)
            opts.add_translation_field(field_name, translation_field)

    # Rebuild information about parents fields. If there are opts.local_fields, field cache would be
    # invalidated (by model._meta.add_field() function). Otherwise, we need to do it manually.
    if len(opts.local_fields) == 0:
        try:
            model._meta._fill_fields_cache()
        except AttributeError:
            # Django 1.8 removed _fill_fields_cache
            model._meta._expire_cache()
            model._meta.get_fields()
Ejemplo n.º 2
0
def add_translation_fields(model, opts):
    """
    Monkey patches the original model class to provide additional fields for
    every language.
    Adds newly created translation fields to the given translation options.
    """
    model_empty_values = getattr(opts, 'empty_values', NONE)
    for field_name in opts.local_fields.keys():
        field_empty_value = parse_field(model_empty_values, field_name, NONE)
        for l in mt_settings.AVAILABLE_LANGUAGES:
            # Create a dynamic translation field
            translation_field = create_translation_field(
                model=model, field_name=field_name, lang=l, empty_value=field_empty_value)
            # Construct the name for the localized field
            localized_field_name = build_localized_fieldname(field_name, l)
            # Check if the model already has a field by that name

            if hasattr(model, localized_field_name):
                # Check if are not dealing with abstract field inherited.
                for cls in model.__mro__:
                    if hasattr(cls, '_meta') and cls.__dict__.get(localized_field_name, None):
                        cls_opts = translator._get_options_for_model(cls)
                        if not cls._meta.abstract or field_name not in cls_opts.local_fields:
                            raise ValueError("Error adding translation field. Model '%s' already"
                                             " contains a field named '%s'." %
                                             (model._meta.object_name, localized_field_name))
            # This approach implements the translation fields as full valid
            # django model fields and therefore adds them via add_to_class
            model.add_to_class(localized_field_name, translation_field)
            opts.add_translation_field(field_name, translation_field)

    # Rebuild information about parents fields. If there are opts.local_fields, field cache would be
    # invalidated (by model._meta.add_field() function). Otherwise, we need to do it manually.
    if len(opts.local_fields) == 0:
        try:
            model._meta._fill_fields_cache()
        except AttributeError:
            # Django 1.8 removed _fill_fields_cache
            model._meta._expire_cache()
            model._meta.get_fields()
Ejemplo n.º 3
0
    def register(self, model_or_iterable, opts_class=None, **options):
        """
        Registers the given model(s) with the given translation options.

        The model(s) should be Model classes, not instances.

        Fields declared for translation on a base class are inherited by
        subclasses. If the model or one of its subclasses is already
        registered for translation, this will raise an exception.
        """
        if isinstance(model_or_iterable, ModelBase):
            model_or_iterable = [model_or_iterable]

        for model in model_or_iterable:
            # Ensure that a base is not registered after a subclass (_registry
            # is closed with respect to taking bases, so we can just check if
            # we've seen the model).
            if model in self._registry:
                if self._registry[model].registered:
                    raise AlreadyRegistered(
                        'Model "%s" is already registered for translation' %
                        model.__name__)
                else:
                    descendants = [
                        d.__name__ for d in self._registry.keys()
                        if issubclass(d, model) and d != model
                    ]
                    raise DescendantRegistered(
                        'Model "%s" cannot be registered after its subclass'
                        ' "%s"' % (model.__name__, descendants[0]))

            # Find inherited fields and create options instance for the model.
            opts = self._get_options_for_model(model, opts_class, **options)

            # Now, when all fields are initialized and inherited, validate configuration.
            opts.validate()

            # Mark the object explicitly as registered -- registry caches
            # options of all models, registered or not.
            opts.registered = True

            # Add translation fields to the model.
            if model._meta.proxy:
                delete_cache_fields(model)
            else:
                add_translation_fields(model, opts)

            # Delete all fields cache for related model (parent and children)
            related = ((f for f in model._meta.get_fields()
                        if (f.one_to_many or f.one_to_one) and f.auto_created)
                       if NEW_RELATED_API else
                       model._meta.get_all_related_objects())
            for related_obj in related:
                delete_cache_fields(related_obj.model)

            # Set MultilingualManager
            add_manager(model)

            # Patch __init__ to rewrite fields
            patch_constructor(model)

            # Connect signal for model
            post_init.connect(delete_mt_init, sender=model)

            # Patch clean_fields to verify form field clearing
            patch_clean_fields(model)

            # Patch __metaclass__ and other methods to allow deferring to work
            patch_metaclass(model)
            patch_get_deferred_fields(model)

            # Substitute original field with descriptor
            model_fallback_languages = getattr(opts, 'fallback_languages',
                                               None)
            model_fallback_values = getattr(opts, 'fallback_values', NONE)
            model_fallback_undefined = getattr(opts, 'fallback_undefined',
                                               NONE)
            for field_name in opts.local_fields.keys():
                field = model._meta.get_field(field_name)
                field_fallback_value = parse_field(model_fallback_values,
                                                   field_name, NONE)
                field_fallback_undefined = parse_field(
                    model_fallback_undefined, field_name, NONE)
                descriptor = TranslationFieldDescriptor(
                    field,
                    fallback_languages=model_fallback_languages,
                    fallback_value=field_fallback_value,
                    fallback_undefined=field_fallback_undefined)
                setattr(model, field_name, descriptor)
                if isinstance(field, ForeignKey):
                    # We need to use a special descriptor so that
                    # _id fields on translated ForeignKeys work
                    # as expected.
                    desc = TranslatedRelationIdDescriptor(
                        field_name, model_fallback_languages)
                    setattr(model, field.get_attname(), desc)

                    # Set related field names on other model
                    if NEW_RELATED_API and not field.remote_field.is_hidden():
                        other_opts = self._get_options_for_model(
                            field.remote_field.to)
                        other_opts.related = True
                        other_opts.related_fields.append(
                            field.related_query_name())
                        # Add manager in case of non-registered model
                        add_manager(field.remote_field.to)
                    elif not NEW_RELATED_API and not field.rel.is_hidden():
                        other_opts = self._get_options_for_model(field.rel.to)
                        other_opts.related = True
                        other_opts.related_fields.append(
                            field.related_query_name())
                        add_manager(
                            field.rel.to
                        )  # Add manager in case of non-registered model

                if isinstance(field, OneToOneField):
                    # Fix translated_field caching for SingleRelatedObjectDescriptor
                    sro_descriptor = (
                        getattr(field.remote_field.to,
                                field.remote_field.get_accessor_name())
                        if NEW_RELATED_API else getattr(
                            field.rel.to, field.related.get_accessor_name()))
                    patch_related_object_descriptor_caching(sro_descriptor)
    def register(self, model_or_iterable, opts_class=None, **options):
        """
        Registers the given model(s) with the given translation options.

        The model(s) should be Model classes, not instances.

        Fields declared for translation on a base class are inherited by
        subclasses. If the model or one of its subclasses is already
        registered for translation, this will raise an exception.
        """
        if isinstance(model_or_iterable, ModelBase):
            model_or_iterable = [model_or_iterable]

        for model in model_or_iterable:
            # Ensure that a base is not registered after a subclass (_registry
            # is closed with respect to taking bases, so we can just check if
            # we've seen the model).
            if model in self._registry:
                if self._registry[model].registered:
                    raise AlreadyRegistered(
                        'Model "%s" is already registered for translation' %
                        model.__name__)
                else:
                    descendants = [d.__name__ for d in self._registry.keys()
                                   if issubclass(d, model) and d != model]
                    raise DescendantRegistered(
                        'Model "%s" cannot be registered after its subclass'
                        ' "%s"' % (model.__name__, descendants[0]))

            # Find inherited fields and create options instance for the model.
            opts = self._get_options_for_model(model, opts_class, **options)

            # Now, when all fields are initialized and inherited, validate configuration.
            opts.validate()

            # Mark the object explicitly as registered -- registry caches
            # options of all models, registered or not.
            opts.registered = True

            # Add translation fields to the model.
            if model._meta.proxy:
                delete_cache_fields(model)
            else:
                add_translation_fields(model, opts)

            # Delete all fields cache for related model (parent and children)
            for related_obj in model._meta.get_all_related_objects():
                delete_cache_fields(related_obj.model)

            # Set MultilingualManager
            add_manager(model)

            # Patch __init__ to rewrite fields
            patch_constructor(model)

            # Connect signal for model
            post_init.connect(delete_mt_init, sender=model)

            # Patch clean_fields to verify form field clearing
            patch_clean_fields(model)

            # Patch __metaclass__ and other methods to allow deferring to work
            patch_metaclass(model)
            patch_get_deferred_fields(model)

            # Substitute original field with descriptor
            model_fallback_languages = getattr(opts, 'fallback_languages', None)
            model_fallback_values = getattr(opts, 'fallback_values', NONE)
            model_fallback_undefined = getattr(opts, 'fallback_undefined', NONE)
            for field_name in opts.local_fields.keys():
                field = model._meta.get_field(field_name)
                field_fallback_value = parse_field(model_fallback_values, field_name, NONE)
                field_fallback_undefined = parse_field(model_fallback_undefined, field_name, NONE)
                descriptor = TranslationFieldDescriptor(
                    field,
                    fallback_languages=model_fallback_languages,
                    fallback_value=field_fallback_value,
                    fallback_undefined=field_fallback_undefined)
                setattr(model, field_name, descriptor)
                if isinstance(field, ForeignKey):
                    # We need to use a special descriptor so that
                    # _id fields on translated ForeignKeys work
                    # as expected.
                    desc = TranslatedRelationIdDescriptor(field_name, model_fallback_languages)
                    setattr(model, field.get_attname(), desc)

                    # Set related field names on other model
                    if not field.rel.is_hidden():
                        other_opts = self._get_options_for_model(field.rel.to)
                        other_opts.related = True
                        other_opts.related_fields.append(field.related_query_name())
                        add_manager(field.rel.to)  # Add manager in case of non-registered model

                if isinstance(field, OneToOneField):
                    # Fix translated_field caching for SingleRelatedObjectDescriptor
                    sro_descriptor = getattr(field.rel.to, field.related.get_accessor_name())
                    patch_related_object_descriptor_caching(sro_descriptor)
Ejemplo n.º 5
0
    def _register_single_model(self, model, opts):
        # Now, when all fields are initialized and inherited, validate configuration.
        opts.validate()

        # Mark the object explicitly as registered -- registry caches
        # options of all models, registered or not.
        opts.registered = True

        # Add translation fields to the model.
        if model._meta.proxy:
            delete_cache_fields(model)
        else:
            add_translation_fields(model, opts)

        # Delete all fields cache for related model (parent and children)
        related = ((
            f for f in model._meta.get_fields()
            if (f.one_to_many or f.one_to_one) and
            f.auto_created
        ) if NEW_RELATED_API else model._meta.get_all_related_objects())

        for related_obj in related:
            delete_cache_fields(related_obj.model)

        # Set MultilingualManager
        add_manager(model)

        # Patch __init__ to rewrite fields
        patch_constructor(model)

        # Connect signal for model
        if NEW_DEFERRED_API:
            post_init.connect(delete_mt_init, sender=model)
        else:
            # deferred models have their own classes and the `sender` does not match.
            # Connect signal for all models.
            post_init.connect(delete_mt_init, dispatch_uid="wagtail_modeltranslation")

        # Patch clean_fields to verify form field clearing
        patch_clean_fields(model)

        # Patch __metaclass__ and other methods to allow deferring to work
        if not NEW_DEFERRED_API:
            patch_metaclass(model)
        patch_get_deferred_fields(model)
        patch_refresh_from_db(model)

        # Substitute original field with descriptor
        model_fallback_languages = getattr(opts, 'fallback_languages', None)
        model_fallback_values = getattr(opts, 'fallback_values', NONE)
        model_fallback_undefined = getattr(opts, 'fallback_undefined', NONE)
        for field_name in opts.local_fields.keys():
            field = model._meta.get_field(field_name)
            field_fallback_value = parse_field(model_fallback_values, field_name, NONE)
            field_fallback_undefined = parse_field(model_fallback_undefined, field_name, NONE)
            descriptor = TranslationFieldDescriptor(
                field,
                fallback_languages=model_fallback_languages,
                fallback_value=field_fallback_value,
                fallback_undefined=field_fallback_undefined)
            setattr(model, field_name, descriptor)
            if isinstance(field, ForeignKey):
                # We need to use a special descriptor so that
                # _id fields on translated ForeignKeys work
                # as expected.
                desc = TranslatedRelationIdDescriptor(field_name, model_fallback_languages)
                setattr(model, field.get_attname(), desc)

                # Set related field names on other model
                if NEW_RELATED_API and not field.remote_field.is_hidden():
                    other_opts = self._get_options_for_model(field.remote_field.to)
                    other_opts.related = True
                    other_opts.related_fields.append(field.related_query_name())
                    # Add manager in case of non-registered model
                    add_manager(field.remote_field.to)
                elif not NEW_RELATED_API and not field.rel.is_hidden():
                    other_opts = self._get_options_for_model(field.rel.to)
                    other_opts.related = True
                    other_opts.related_fields.append(field.related_query_name())
                    add_manager(field.rel.to)  # Add manager in case of non-registered model

            if isinstance(field, OneToOneField):
                # Fix translated_field caching for SingleRelatedObjectDescriptor
                sro_descriptor = (
                    getattr(field.remote_field.to, field.remote_field.get_accessor_name())
                    if NEW_RELATED_API
                    else getattr(field.rel.to, field.related.get_accessor_name()))
                patch_related_object_descriptor_caching(sro_descriptor)