i18n base model for translatable content

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from django.db.models.base import ModelBase
from django.db.models.base import Model
from django.db import models
from django.conf import settings


# Returns class object from a specified module
def getclass(classname, modulename):
    from_module = __import__(modulename, globals(), locals(), classname)
    return getattr(from_module, classname)

class I18NBase(ModelBase):
    def __new__(cls, name, bases, attrs):
        try:
            if I18NModel in bases:
                attr_meta = attrs.pop('Meta', None)
                # Find out if `i18n_common_model` is defined in `class Meta`,
                # and use that. Otherwise, use this class name -4 chars:
                common_classname = getattr(attr_meta, 'i18n_common_model',
                                           name[:-4])
                I18NCommonModel = getclass(common_classname, attrs['__module__'])
                attrs['i18n_common'] = models.ForeignKey(I18NCommonModel)
                attrs['lang'] = models.CharField(max_length = 5,
                                                 choices = settings.LANGUAGES)
        except NameError:
            pass
        return ModelBase.__new__(cls, name, bases, attrs)


class I18NModel(Model):
    __metaclass__ = I18NBase

    class Meta:
        abstract = True

More like this

  1. Extended i18n base model by alcinnz 1 year ago
  2. PositionField by jpwatts 5 years, 9 months ago
  3. Multiple inheritance of newforms and modelforms by simon 6 years ago
  4. Modifying the fields of a third/existing model class by marinho 3 years, 3 months ago
  5. render_markup filter, specify the markup filter as a string by exogen 7 years ago

Comments

izibi (on July 5, 2008):

I would import the settings like this:

from django.conf import settings

#

foxbunny (on July 5, 2008):

Heh, and there also a typo there. Thanks. I'll fix that.

#

willhardy (on July 7, 2008):

Why not use model inheritance to do the same thing?

class Article(models.Model):
    author = models.CharField(max_length = 40)


class TranslatedArticle(Article):
    language = models.CharField(max_length = 5, choices=settings.LANGUAGES)
    title = models.CharField(max_length = 120)
    body = models.TextField()

If you really wanted, you could create a mixin to keep things cleaner, and automatically add the language field eg:

class Article(models.Model):
    author = models.CharField(max_length = 40)


class TranslatedArticle(Article, TranslationModel):
    title = models.CharField(max_length = 120)
    body = models.TextField()

But I like to keep things explicit :-)

#

willhardy (on July 8, 2008):

Good call, that's exactly the case. Your approach is now my favourite.

#

foxbunny (on July 9, 2008):

It is now possible to define i18n_common_model attribute in class Meta section. Here's an example:

class Meta: 
    i18n_common_model = "MyCommonModel"

#

foxbunny (on July 9, 2008):

The above makes it a bit more explicit if you want that. I might make it a requirement in future.

#

foxbunny (on July 10, 2008):

I've added

class Meta:
    abstract = True

to the I18NModel. It seems it has to be there or this thing fails when deleting models.

#

mirobe (on August 21, 2008):

Can you please write about usage / querying for values in views/templates ?

Thanks!

#

foxbunny (on August 23, 2008):

I've seen the project you mentioned, but it's a totally different philosophy.

The querying of I18N models is the same as any other model. You just keep in mind that you have a many-to-1 link from the I18N model to the non-I18N models. The I18N model has a field called i18n_common, which you use to find the non-translated (non-translatable) fields (see line 23). The language of the I18N model is defined by the lang field (line 24).

What this code does is basically just add the above two fields into the I18N model based on the name of the model. Nothing more, and nothing less. The rest is up to you. You do with them what you'd do with related models.

#

foxbunny (on August 23, 2008):

Oh, and btw, this has nothing to do with the Admin app. The admin app will treat the two models just like any two models. If someone's interested in writing the admin part so that admin will use the two models as one, that's cool, but I don't have the skills yet.

#

(Forgotten your password?)