Together with my mentor, Dusty Phillips, I have developed a simple class that dynamically adds two fields to its subclasses.
This is useful in cases when a single piece of content is divided into translatable and non-translatable fields, connected by a 1-to-many relationship.
Update 2009/03/30
Since its inception, this snippet has grown into a significantly more powerful solution for translatable content (I use it myself with great joy :). The project is now hosted on github:
Update 2008/07/09
It is now possible to define i18n_common_model
attribute in class Meta
section. Here's an example:
class Meta:
i18n_common_model = "MyCommonModel"
As you can see, it has to be a string, not the real class, and it is case-sensitive.
Example
class Article(models.Model):
author = models.CharField(max_length = 40)
class Admin:
pass
class ArticleI18N(I18NModel):
title = models.CharField(max_length = 120)
body = models.TextField()
class Admin:
pass
# optionally, you can specify the base class
# if it doesn't follow the naming convention:
#
# class Meta:
# i18m_common_model = "Article"
When the ArticleI18N class is created, it automatically gains two new fields. lang
field is a CharField with choices limited to either settings.LANGUAGES
or django.conf.global_settings.LANGUAGES
. The other field is i18n_common
field which is a ForeignKey to Article model.
The conventions
-
call the translation model
SomeBaseModelI18N
, and the non-translation model SomeBaseModel (i.e., the translation model is called basename+"I18N") -
the first convention can be overriden by specifying the base model name using the
i18n_common_model
attribute inMeta
section of theI18N
model -
I18N model is a subclass of
I18NModel
class
Original blog post
http://blog.papa-studio.com/2008/07/04/metaclasses-and-translations/
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
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
I would import the settings like this:
from django.conf import settings
#
Heh, and there also a typo there. Thanks. I'll fix that.
#
Why not use model inheritance to do the same thing?
If you really wanted, you could create a mixin to keep things cleaner, and automatically add the language field eg:
But I like to keep things explicit :-)
#
Good call, that's exactly the case. Your approach is now my favourite.
#
It is now possible to define i18n_common_model attribute in class Meta section. Here's an example:
#
The above makes it a bit more explicit if you want that. I might make it a requirement in future.
#
I've added
to the
I18NModel
. It seems it has to be there or this thing fails when deleting models.#
Can you please write about usage / querying for values in views/templates ?
Thanks!
#
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 thelang
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.
#
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.
#
Please login first before commenting.