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
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
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
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
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
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
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
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