Source code for slim.models.fields

__title__ = 'slim.models.fields'
__version__ = '0.7'
__build__ = 0x000007
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__all__ = ('LanguageField', 'SimpleLanguageField')

from six import PY2

from django.db import models
from django.core import exceptions
from django.utils.translation import ugettext_lazy as _

from slim import get_languages, default_language, get_languages_keys
from slim.settings import ENABLE_MONKEY_PATCHING
from slim.monkey_patches import monkeypatch_method, monkeypatch_property
from slim.helpers import admin_change_url, admin_add_url

[docs]class LanguageField(models.CharField): """ LanguageField model. Stores language string in a ``CharField`` field. Using `contrib_to_class` melthod adds `translation_of` field, which is simply a ``ForeignKey`` to the same class. """ __metaclass__ = models.SubfieldBase def __init__(self, *args, **kwargs): """ Create new field. Argument ``populate`` will be sent as-is to the form field. """ defaults = { 'verbose_name': _('Language'), 'populate': None, 'max_length': 10, 'choices': get_languages(), 'default': default_language } defaults.update(kwargs) self.populate = defaults.pop('populate', None) super(LanguageField, self).__init__(*args, **defaults)
[docs] def formfield(self, **kwargs): """ Returns best form field to represent this model field """ defaults = { 'form_class': LanguageField, 'populate': self.populate, } defaults.update(kwargs) return super(models.CharField, self).formfield(**defaults)
[docs] def validate(self, value, model_instance): """ Validating the field. We shall make sure that there are double translations for the same language for the same object. That's why, in case if model is not yet saved (``translated_object`` does not yet have a primary key), we check if there are already translations of the same object in the language we specify now. Otherwise, if ``model_instance`` already has a primary key, we anyway try to get a ``translated_object`` and compare it with our ``model_instance``. In case if ``translated_object`` exists and not equal to our ``model_instance`` we raise an error. NOTE: This has nothing to do with unique fields in the original ``model_instance``. Make sure you have properly specified all unique attributes with respect to ``LanguageField` of your original ``model_instance`` if you need those records to be unique. """ if model_instance.original_translation: translated_object = model_instance.original_translation.get_translation_for(value) else: translated_object = None if not model_instance.pk: if translated_object and translated_object.pk: raise exceptions.ValidationError("Translation in language %s for this object already exists." % value) else: if translated_object and translated_object.pk and model_instance != translated_object: raise exceptions.ValidationError("Translation in language %s for this object already exists." % value) super(LanguageField, self).validate(value, model_instance)
[docs] def contribute_to_class(self, cls, name): """ Language field consists of more than one database record. We have ``lanaguage`` (CharField) and ``translation_of`` (ForeignKey to ``cls``) in order to identify translated and primary objects. We have a set of very useful methods implemented in order to get translations easily. """ self.name = name self.translation_of = models.ForeignKey(cls, blank=True, null=True, verbose_name=_('Translation of'), \ related_name='translations', \ limit_choices_to={'language': default_language}, \ help_text=_('Leave this empty for entries in the primary language.')) cls.add_to_class('translation_of', self.translation_of) super(LanguageField, self).contribute_to_class(cls, name) if ENABLE_MONKEY_PATCHING: @monkeypatch_property(cls) def is_multilingual(self): """ Simple flat to use on objects to find our wheither they are multilinugal or not :return bool: Always returns boolean True """ return True @monkeypatch_method(cls) def get_redirect_to_target(self, request): """ Find an acceptable redirect target. If this is a local link, then try to find the page this redirect references and translate it according to the user's language. This way, one can easily implement a localized "/"-url to welcome page redirection. """ target = self.redirect_to if target and target.find('//') == -1: # Not an offsite link http://bla/blubb try: page = cls.objects.page_for_path(target) page = page.get_translation(getattr(request, 'LANGUAGE_CODE', None)) target = page.get_absolute_url() except cls.DoesNotExist: pass return target @monkeypatch_method(cls) def available_translations(self): """ Returns available translations. :return interable: At this moment a list of objects. """ if not self.id: # New, unsaved pages have no translations return [] if is_primary_language(self.language): return self.translations.all() elif self.translation_of: return [self.translation_of] + list(self.translation_of.translations.exclude( language=self.language)) else: return [] @monkeypatch_method(cls) def get_original_translation(self, *args, **kwargs): """ Gets original translation of current object. :return obj: Object of the same class as the one queried. """ if is_primary_language(self.language): return self return self.translation_of @monkeypatch_method(cls) def translation_admin(self, *args, **kwargs): """ Gets a HTML with URL to the original translation of available. For admin use. :return str: """ if self.translation_of: if PY2: url_title = unicode(self.translation_of) else: url_title = self.translation_of return admin_change_url( self._meta.app_label, self._meta.module_name, self.translation_of.id, url_title = url_title ) return '' translation_admin.allow_tags = True translation_admin.short_description = _('Translation of') @monkeypatch_method(cls) def _available_translations_admin(self, include_self=True): """ Gets a HTML with all available translation URLs for current object if available. For admin use. :return str: """ try: original_translation = self.original_translation available_translations = list(self.available_translations()) languages_keys = get_languages_keys() languages = dict(get_languages()) if include_self: available_translations.append(self) output = [] # Processing all available translations. Adding edit links. if available_translations: for translation in available_translations: if PY2: url_title = unicode(languages[translation.language]) else: url_title = languages[translation.language] output.append( admin_change_url( translation._meta.app_label, translation._meta.module_name, translation.id, url_title = url_title ) ) languages_keys.remove(translation.language) if self.pk and self.language in languages_keys: languages_keys.remove(self.language) # For all languages that are still available (original object has no translations for) for language in languages_keys: url = admin_add_url( self._meta.app_label, self._meta.module_name, '?translation_of=%s&amp;language=%s' % (str(original_translation.id), language) ) if PY2: name = unicode(languages[language]) else: name = languages[language] output.append(u'<a href="%(url)s" style="color:#baa">%(name)s</a>' % {'url': url, 'name': name}) return ' | '.join(output) except Exception as e: return '' @monkeypatch_method(cls) def available_translations_admin(self, *args, **kwargs): """ Gets a HTML with all available translation URLs for current object if available. For admin use. :return str: """ return self._available_translations_admin(include_self=True, *args, **kwargs) available_translations_admin.allow_tags = True available_translations_admin.short_description = _('Translations') @monkeypatch_method(cls) def available_translations_exclude_current_admin(self, *args, **kwargs): """ Same as `available_translations_admin` but does not include itself to the list. :return str: """ return self._available_translations_admin(include_self=False, *args, **kwargs) available_translations_exclude_current_admin.allow_tags = True available_translations_exclude_current_admin.short_description = _('Translations') @monkeypatch_property(cls) def original_translation(self): """ Property for ``get_original_translation`` method. :return obj: Object of the same class as the one queried. """ return self.get_original_translation() @monkeypatch_method(cls) def get_translation_for(self, language): """ Get translation article in given language. :param str language: Which shall be one of the languages specified in ``LANGUAGES`` in `settings.py`. :return obj: Either object of the same class as or None if no translations are available for the given ``language``. """ if not language in get_languages_keys(): return None if str(self.language) == str(language): return self if str(self.original_translation.language) == str(language): return self.original_translation try: return self.original_translation.translations.get(language=language) except Exception as e: return None
[docs]class SimpleLanguageField(models.CharField): """ SimpleLanguageField model. Stores language string in a ``CharField`` field. """ def __init__(self, *args, **kwargs): """ Create new field. Argument ``populate`` will be sent as-is to the form field. """ defaults = { 'verbose_name': _('Language'), 'populate': None, 'max_length': 10, 'choices': get_languages(), 'default': default_language } defaults.update(kwargs) self.populate = defaults.pop('populate', None) super(SimpleLanguageField, self).__init__(*args, **defaults)
[docs] def formfield(self, **kwargs): """ Returns best form field to represent this model field """ defaults = { 'form_class': SimpleLanguageField, 'populate': self.populate, } defaults.update(kwargs) return super(models.CharField, self).formfield(**defaults) # Add schema's for South
try: from south.modelsinspector import add_introspection_rules add_introspection_rules( rules=[((LanguageField,), [], {'populate': ('populate', {'default':None}),})], \ patterns=['slim.models\.fields\.LanguageField', 'slim.models\.fields\.SimpleLanguageField'] ) except ImportError: pass
Read the Docs v: 0.7
Versions
latest
0.7.2
0.7.1
0.7
0.6
0.5
Downloads
On Read the Docs
Project Home
Builds

Free document hosting provided by Read the Docs.