Example #1
0
def create_translations_model(model, related_name, meta, **fields):
    """
    Create the translations model for the shared model 'model'.
    'related_name' is the related name for the reverse FK from the translations
    model.
    'meta' is a (optional) dictionary of attributes for the translations model's
    inner Meta class.
    'fields' is a dictionary of fields to put on the translations model.
    
    Two fields are enforced on the translations model:
    
        language_code: A 15 char, db indexed field.
        master: A ForeignKey back to the shared model.
        
    Those two fields are unique together, this get's enforced in the inner Meta
    class of the translations table
    """
    if not meta:
        meta = {}
    unique = [('language_code', 'master')]
    meta['unique_together'] = list(meta.get('unique_together', [])) + unique
    # Create inner Meta class
    Meta = type('Meta', (object, ), meta)
    if not hasattr(Meta, 'db_table'):
        Meta.db_table = model._meta.db_table + '%stranslation' % getattr(
            settings, 'NANI_TABLE_NAME_SEPARATOR', '_')
    Meta.app_label = model._meta.app_label
    name = '%sTranslation' % model.__name__
    attrs = {}
    attrs.update(fields)
    attrs['Meta'] = Meta
    attrs['__module__'] = model.__module__
    attrs['objects'] = TranslationsModelManager()
    attrs['language_code'] = models.CharField(max_length=15, db_index=True)
    # null=True is so we can prevent cascade deletion
    attrs['master'] = models.ForeignKey(model,
                                        related_name=related_name,
                                        editable=False,
                                        null=True)
    # Create and return the new model
    translations_model = ModelBase(name, (BaseTranslationModel, ), attrs)
    bases = (
        model.DoesNotExist,
        translations_model.DoesNotExist,
    )
    DNE = type('DoesNotExist', bases, {})
    translations_model.DoesNotExist = DNE
    opts = translations_model._meta
    opts.shared_model = model

    # Register it as a global in the shared model's module.
    # This is needed so that Translation model instances, and objects which
    # refer to them, can be properly pickled and unpickled. The Django session
    # and caching frameworks, in particular, depend on this behaviour.
    mod = sys.modules[model.__module__]
    setattr(mod, name, translations_model)

    return translations_model
Example #2
0
    def create_translations_model(self, model, related_name):
        """ Create the translations model for a shared model.
            model -- the model class to create translations for
            related_name -- the related name for the reverse FK from the translations model.
        """
        model_name = '%sTranslation' % model.__name__
        translation_bases, translation_base_fields = self._scan_model_bases(
            model)

        attrs = self.fields.copy()
        attrs.update({
            'Meta':
            self._build_meta_class(
                model,
                translation_base_fields.union(self.fields).union(
                    ('language_code', ))),
            '__module__':
            model.__module__,
        })

        if not model._meta.abstract:
            attrs.update({
                # If this class is abstract, we must not contribute management fields
                'objects':
                TranslationsModelManager(),
                'language_code':
                models.CharField(max_length=15, db_index=True),
                # Nullable so we can prevent cascade deletion
                'master':
                models.ForeignKey(model,
                                  related_name=related_name,
                                  editable=False,
                                  null=True,
                                  on_delete=models.CASCADE),
            })

        # Create the new model
        if self.base_class:
            translation_bases.insert(0, self.base_class)
        translations_model = ModelBase(model_name, tuple(translation_bases),
                                       attrs)
        translations_model._meta.shared_model = model
        if not model._meta.abstract:
            # Abstract models do not have a DNE class
            bases = (
                model.DoesNotExist,
                translations_model.DoesNotExist,
            )
            translations_model.DoesNotExist = type('DoesNotExist', bases, {})

        # Register it as a global in the shared model's module.
        # This is needed so that Translation model instances, and objects which
        # refer to them, can be properly pickled and unpickled. The Django session
        # and caching frameworks, in particular, depend on this behaviour.
        setattr(sys.modules[model.__module__], model_name, translations_model)
        return translations_model
Example #3
0
def create_translations_model(model, related_name, meta, **fields):
    """
    Create the translations model for the shared model 'model'.
    'related_name' is the related name for the reverse FK from the translations
    model.
    'meta' is a (optional) dictionary of attributes for the translations model's
    inner Meta class.
    'fields' is a dictionary of fields to put on the translations model.
    
    Two fields are enforced on the translations model:
    
        language_code: A 15 char, db indexed field.
        master: A ForeignKey back to the shared model.
        
    Those two fields are unique together, this get's enforced in the inner Meta
    class of the translations table
    """
    if not meta:
        meta = {}
    unique = [('language_code', 'master')]
    meta['unique_together'] = list(meta.get('unique_together', [])) + unique
    # Create inner Meta class 
    Meta = type('Meta', (object,), meta)
    if not hasattr(Meta, 'db_table'):
        Meta.db_table = model._meta.db_table + '%stranslation' % getattr(settings, 'NANI_TABLE_NAME_SEPARATOR', '_')
    Meta.app_label = model._meta.app_label
    name = '%sTranslation' % model.__name__
    attrs = {}
    attrs.update(fields)
    attrs['Meta'] = Meta
    attrs['__module__'] = model.__module__
    attrs['objects'] = TranslationsModelManager()
    attrs['language_code'] = models.CharField(max_length=15, db_index=True)
    # null=True is so we can prevent cascade deletion
    attrs['master'] = models.ForeignKey(model, related_name=related_name,
                                        editable=False, null=True)
    # Create and return the new model
    translations_model = ModelBase(name, (BaseTranslationModel,), attrs)
    bases = (model.DoesNotExist, translations_model.DoesNotExist,)
    DNE = type('DoesNotExist', bases, {})
    translations_model.DoesNotExist = DNE
    opts = translations_model._meta
    opts.shared_model = model

    # Register it as a global in the shared model's module.
    # This is needed so that Translation model instances, and objects which
    # refer to them, can be properly pickled and unpickled. The Django session
    # and caching frameworks, in particular, depend on this behaviour.
    mod = sys.modules[model.__module__]
    setattr(mod, name, translations_model)

    return translations_model
Example #4
0
def create_translations_model(model, related_name, meta, **fields):
    """
    Create the translations model for the shared model 'model'.
    'related_name' is the related name for the reverse FK from the translations
    model.
    'meta' is a (optional) dictionary of attributes for the translations model's
    inner Meta class.
    'fields' is a dictionary of fields to put on the translations model.
    
    Two fields are enforced on the translations model:
    
        language_code: A 15 char, db indexed field.
        master: A ForeignKey back to the shared model.
        
    Those two fields are unique together, this get's enforced in the inner Meta
    class of the translations table
    """
    if not meta:
        meta = {}
    unique = [('language_code', 'master')]
    meta['unique_together'] = list(meta.get('unique_together', [])) + unique
    # Create inner Meta class
    Meta = type('Meta', (object, ), meta)
    if not hasattr(Meta, 'db_table'):
        Meta.db_table = model._meta.db_table + '%stranslation' % getattr(
            settings, 'NANI_TABLE_NAME_SEPARATOR', '_')
    name = '%sTranslation' % model.__name__
    attrs = {}
    attrs.update(fields)
    attrs['Meta'] = Meta
    attrs['__module__'] = model.__module__
    attrs['objects'] = TranslationsModelManager()
    attrs['language_code'] = models.CharField(max_length=15, db_index=True)
    # null=True is so we can prevent cascade deletion
    attrs['master'] = models.ForeignKey(model,
                                        related_name=related_name,
                                        editable=False,
                                        null=True)
    # Create and return the new model
    translations_model = ModelBase(name, (BaseTranslationModel, ), attrs)
    bases = (
        model.DoesNotExist,
        translations_model.DoesNotExist,
    )
    DNE = type('DoesNotExist', bases, {})
    translations_model.DoesNotExist = DNE
    opts = translations_model._meta
    opts.shared_model = model
    return translations_model
Example #5
0
    def create_translations_model(self, model, related_name):
        """ Create the translations model for a shared model.
            model -- the model class to create translations for
            related_name -- the related name for the reverse FK from the translations model.
        """
        model_name = "%sTranslation" % model.__name__
        translation_bases, translation_base_fields = self._scan_model_bases(model)

        attrs = self.fields.copy()
        attrs.update(
            {
                "Meta": self._build_meta_class(
                    model, translation_base_fields.union(self.fields).union(("language_code",))
                ),
                "__module__": model.__module__,
            }
        )

        if not model._meta.abstract:
            attrs.update(
                {
                    # If this class is abstract, we must not contribute management fields
                    "objects": TranslationsModelManager(),
                    "language_code": models.CharField(max_length=15, db_index=True),
                    # Nullable so we can prevent cascade deletion
                    "master": models.ForeignKey(
                        model, related_name=related_name, editable=False, null=True, on_delete=models.CASCADE
                    ),
                }
            )

        # Create the new model
        if self.base_class:
            translation_bases.insert(0, self.base_class)
        translations_model = ModelBase(model_name, tuple(translation_bases), attrs)
        translations_model._meta.shared_model = model
        if not model._meta.abstract:
            # Abstract models do not have a DNE class
            bases = (model.DoesNotExist, translations_model.DoesNotExist)
            translations_model.DoesNotExist = type("DoesNotExist", bases, {})

        # Register it as a global in the shared model's module.
        # This is needed so that Translation model instances, and objects which
        # refer to them, can be properly pickled and unpickled. The Django session
        # and caching frameworks, in particular, depend on this behaviour.
        setattr(sys.modules[model.__module__], model_name, translations_model)
        return translations_model
Example #6
0
def create_translations_model(model, related_name, meta, **fields):
    """
    Create the translations model for the shared model 'model'.
    'related_name' is the related name for the reverse FK from the translations
    model.
    'meta' is a (optional) dictionary of attributes for the translations model's
    inner Meta class.
    'fields' is a dictionary of fields to put on the translations model.
    
    Two fields are enforced on the translations model:
    
        language_code: A 15 char, db indexed field.
        master: A ForeignKey back to the shared model.
        
    Those two fields are unique together, this get's enforced in the inner Meta
    class of the translations table
    """
    if not meta:
        meta = {}
    unique = [('language_code', 'master')]
    meta['unique_together'] = list(meta.get('unique_together', [])) + unique
    # Create inner Meta class 
    Meta = type('Meta', (object,), meta)
    if not hasattr(Meta, 'db_table'):
        Meta.db_table = model._meta.db_table + '%stranslation' % getattr(settings, 'NANI_TABLE_NAME_SEPARATOR', '_')
    name = '%sTranslation' % model.__name__
    attrs = {}
    attrs.update(fields)
    attrs['Meta'] = Meta
    attrs['__module__'] = model.__module__
    attrs['objects'] = TranslationsModelManager()
    attrs['language_code'] = models.CharField(max_length=15, db_index=True)
    # null=True is so we can prevent cascade deletion
    attrs['master'] = models.ForeignKey(model, related_name=related_name,
                                        editable=False, null=True)
    # Create and return the new model
    translations_model = ModelBase(name, (BaseTranslationModel,), attrs)
    bases = (model.DoesNotExist, translations_model.DoesNotExist,)
    DNE = type('DoesNotExist', bases, {})
    translations_model.DoesNotExist = DNE
    opts = translations_model._meta
    opts.shared_model = model
    return translations_model
Example #7
0
def create_translations_model(model, related_name, meta, **fields):
    """
    Create the translations model for the shared model 'model'.
    'related_name' is the related name for the reverse FK from the translations
    model.
    'meta' is a (optional) dictionary of attributes for the translations model's
    inner Meta class.
    'fields' is a dictionary of fields to put on the translations model.
    
    Two fields are enforced on the translations model:
    
        language_code: A 15 char, db indexed field.
        master: A ForeignKey back to the shared model.
        
    Those two fields are unique together, this get's enforced in the inner Meta
    class of the translations table
    """

    # Build a list of translation models from base classes. Depth-first scan.
    abstract = model._meta.abstract
    translation_bases = []
    translation_base_fields = set()
    scan_bases = list(reversed(model.__bases__)) # backwards so we can use pop/extend
    while scan_bases:
        base = scan_bases.pop()
        if not issubclass(base, TranslatableModel) or base is TranslatableModel:
            continue
        if not base._meta.abstract:
            raise TypeError(
                'Multi-table inheritance of translatable models is not supported. '
                'Concrete model %s is not a valid base model for %s.' % (
                    base._meta.model_name if django.VERSION >= (1, 6) else base._meta.module_name,
                    model._meta.model_name if django.VERSION >= (1, 6) else model._meta.module_name))
        try:
            # The base may have translations model, then just inherit that
            translation_bases.append(base._meta.translations_model)
            translation_base_fields.update(field.name for field in base._meta.translations_model._meta.fields)
        except AttributeError:
            # But it may not, and simply inherit other abstract bases, scan them
            scan_bases.extend(reversed(base.__bases__))
    translation_bases.append(BaseTranslationModel)

    # Create translation model Meta
    meta = meta or {}
    meta['abstract'] = abstract
    meta['db_tablespace'] = model._meta.db_tablespace
    meta['managed'] = model._meta.managed
    if model._meta.order_with_respect_to in fields:
        raise ValueError(
            'Using a translated fields in %s.Meta.order_with_respect_to is ambiguous '
            'and hvad does not support it.' %
            model._meta.model_name if django.VERSION >= (1, 6) else model._meta.module_name)

    sconst, tconst = _split_together(
        model._meta.unique_together,
        translation_base_fields.union(fields).union(('language_code',)),
        meta,
        'unique_together'
    )
    model._meta.unique_together = tuple(sconst)
    meta['unique_together'] = tuple(tconst)
    if django.VERSION >= (1, 5):
        sconst, tconst = _split_together(
            model._meta.index_together,
            translation_base_fields.union(fields).union(('language_code',)),
            meta,
            'index_together'
        )
        model._meta.index_together = tuple(sconst)
        meta['index_together'] = tuple(tconst)

    if not abstract:
        unique = [('language_code', 'master')]
        meta['unique_together'] = list(meta.get('unique_together', [])) + unique
    Meta = type('Meta', (object,), meta)

    if not hasattr(Meta, 'db_table'):
        Meta.db_table = model._meta.db_table + '%stranslation' % TABLE_NAME_SEPARATOR
    Meta.app_label = model._meta.app_label
    name = '%sTranslation' % model.__name__

    # Create translation model
    attrs = {}
    attrs.update(fields)
    attrs['Meta'] = Meta
    attrs['__module__'] = model.__module__

    if not abstract:
        # If this class is abstract, we must not contribute management fields
        attrs['objects'] = TranslationsModelManager()
        attrs['language_code'] = models.CharField(max_length=15, db_index=True)
        # null=True is so we can prevent cascade deletion
        attrs['master'] = models.ForeignKey(model, related_name=related_name,
                                            editable=False, null=True)
    # Create and return the new model
    translations_model = ModelBase(name, tuple(translation_bases), attrs)
    if not abstract:
        # Abstract models do not have a DNE class
        bases = (model.DoesNotExist, translations_model.DoesNotExist,)
        DNE = type('DoesNotExist', bases, {})
        translations_model.DoesNotExist = DNE
    opts = translations_model._meta
    opts.shared_model = model

    # We need to set it here so it is available when we scan subclasses
    model._meta.translations_model = translations_model

    # Register it as a global in the shared model's module.
    # This is needed so that Translation model instances, and objects which
    # refer to them, can be properly pickled and unpickled. The Django session
    # and caching frameworks, in particular, depend on this behaviour.
    mod = sys.modules[model.__module__]
    setattr(mod, name, translations_model)

    return translations_model
Example #8
0
def create_translations_model(model, related_name, meta, **fields):
    """
    Create the translations model for the shared model 'model'.
    'related_name' is the related name for the reverse FK from the translations
    model.
    'meta' is a (optional) dictionary of attributes for the translations model's
    inner Meta class.
    'fields' is a dictionary of fields to put on the translations model.
    
    Two fields are enforced on the translations model:
    
        language_code: A 15 char, db indexed field.
        master: A ForeignKey back to the shared model.
        
    Those two fields are unique together, this get's enforced in the inner Meta
    class of the translations table
    """

    # Build a list of translation models from base classes. Depth-first scan.
    abstract = model._meta.abstract
    translation_bases = []
    translation_base_fields = set()
    scan_bases = list(reversed(
        model.__bases__))  # backwards so we can use pop/extend
    while scan_bases:
        base = scan_bases.pop()
        if not issubclass(base,
                          TranslatableModel) or base is TranslatableModel:
            continue
        if not base._meta.abstract:
            raise TypeError(
                'Multi-table inheritance of translatable models is not supported. '
                'Concrete model %s is not a valid base model for %s.' %
                (base._meta.model_name if django.VERSION >=
                 (1, 6) else base._meta.module_name,
                 model._meta.model_name if django.VERSION >=
                 (1, 6) else model._meta.module_name))
        try:
            # The base may have translations model, then just inherit that
            translation_bases.append(base._meta.translations_model)
            translation_base_fields.update(
                field.name
                for field in base._meta.translations_model._meta.fields)
        except AttributeError:
            # But it may not, and simply inherit other abstract bases, scan them
            scan_bases.extend(reversed(base.__bases__))
    translation_bases.append(BaseTranslationModel)

    # Create translation model Meta
    meta = meta or {}
    meta['abstract'] = abstract
    meta['db_tablespace'] = model._meta.db_tablespace
    meta['managed'] = model._meta.managed
    if model._meta.order_with_respect_to in fields:
        raise ValueError(
            'Using a translated fields in %s.Meta.order_with_respect_to is ambiguous '
            'and hvad does not support it.' %
            model._meta.model_name if django.VERSION >= (
                1, 6) else model._meta.module_name)

    sconst, tconst = _split_together(
        model._meta.unique_together,
        translation_base_fields.union(fields).union(('language_code', )), meta,
        'unique_together')
    model._meta.unique_together = tuple(sconst)
    meta['unique_together'] = tuple(tconst)
    if django.VERSION >= (1, 5):
        sconst, tconst = _split_together(
            model._meta.index_together,
            translation_base_fields.union(fields).union(('language_code', )),
            meta, 'index_together')
        model._meta.index_together = tuple(sconst)
        meta['index_together'] = tuple(tconst)

    if not abstract:
        unique = [('language_code', 'master')]
        meta['unique_together'] = list(meta.get('unique_together',
                                                [])) + unique
    Meta = type('Meta', (object, ), meta)

    if not hasattr(Meta, 'db_table'):
        Meta.db_table = model._meta.db_table + '%stranslation' % TABLE_NAME_SEPARATOR
    Meta.app_label = model._meta.app_label
    name = '%sTranslation' % model.__name__

    # Create translation model
    attrs = {}
    attrs.update(fields)
    attrs['Meta'] = Meta
    attrs['__module__'] = model.__module__

    if not abstract:
        # If this class is abstract, we must not contribute management fields
        attrs['objects'] = TranslationsModelManager()
        attrs['language_code'] = models.CharField(max_length=15, db_index=True)
        # null=True is so we can prevent cascade deletion
        attrs['master'] = models.ForeignKey(model,
                                            related_name=related_name,
                                            editable=False,
                                            null=True)
    # Create and return the new model
    translations_model = ModelBase(name, tuple(translation_bases), attrs)
    if not abstract:
        # Abstract models do not have a DNE class
        bases = (
            model.DoesNotExist,
            translations_model.DoesNotExist,
        )
        DNE = type('DoesNotExist', bases, {})
        translations_model.DoesNotExist = DNE
    opts = translations_model._meta
    opts.shared_model = model

    # We need to set it here so it is available when we scan subclasses
    model._meta.translations_model = translations_model

    # Register it as a global in the shared model's module.
    # This is needed so that Translation model instances, and objects which
    # refer to them, can be properly pickled and unpickled. The Django session
    # and caching frameworks, in particular, depend on this behaviour.
    mod = sys.modules[model.__module__]
    setattr(mod, name, translations_model)

    return translations_model