Skip to main content

Implementation of localized model fields using PostgreSQL HStore fields.

Project description

https://scrutinizer-ci.com/g/SectorLabs/django-localized-fields/badges/quality-score.png https://scrutinizer-ci.com/g/SectorLabs/django-localized-fields/badges/coverage.png https://img.shields.io/github/license/SectorLabs/django-localized-fields.svg https://badge.fury.io/py/django-localized-fields.svg

django-localized-fields is an implementation of a field class for Django models that allows the field’s value to be set in multiple languages. It does this by utilizing the hstore type (PostgreSQL specific), which is available as models.HStoreField in Django 1.10.

This package requires Python 3.5 or newer, Django 1.10 or newer and PostgreSQL 9.6 or newer.

Installation

  1. Install the package from PyPi:

    $ pip install django-localized-fields
  2. Add localized_fields and django.contrib.postgres to your INSTALLED_APPS:

    INSTALLED_APPS = [
        ....
    
        'django.contrib.postgres',
        'localized_fields'
    ]
  3. Set the database engine to psqlextra.backend:

    DATABASES = {
        'default': {
            ...
            'ENGINE': 'psqlextra.backend'
        }
    }
  1. Set LANGUAGES` and `LANGUAGE_CODE in your settings:

    LANGUAGE_CODE = 'en' # default language
    LANGUAGES = (
        ('en', 'English'),
        ('nl', 'Dutch'),
        ('ro', 'Romanian')
    )

Usage

Preparation

Inherit your model from LocalizedModel and declare fields on your model as LocalizedField:

from localized_fields.models import LocalizedModel
from localized_fields.fields import LocalizedField


class MyModel(LocalizedModel):
    title = LocalizedField()

django-localized-fields integrates with Django’s i18n system, in order for certain languages to be available you have to correctly configure the LANGUAGES and LANGUAGE_CODE settings:

LANGUAGE_CODE = 'en' # default language
LANGUAGES = (
     ('en', 'English'),
     ('nl', 'Dutch'),
     ('ro', 'Romanian')
)

All the LocalizedField you define now will be available in the configured languages.

Basic usage

new = MyModel()
new.title.en = 'english title'
new.title.nl = 'dutch title'
new.title.ro = 'romanian title'
new.save()

By changing the active language you can control which language is presented:

from django.utils import translation

translation.activate('nl')
print(new.title) # prints 'dutch title'

translation.activate('en')
print(new.title) # prints 'english title'

Or get it in a specific language:

print(new.title.get('en')) # prints 'english title'
print(new.title.get('ro')) # prints 'romanian title'
print(new.title.get()) # whatever language is the primary one

You can also explicitly set a value in a certain language:

new.title.set('en', 'other english title')
new.title.set('nl', 'other dutch title')

new.title.ro = 'other romanian title'

Constraints

Required/Optional

At the moment, it is not possible to select two languages to be marked as required. The constraint is not enforced on a database level.

  • Make the primary language required and the others optional (this is the default):

    class MyModel(LocalizedModel):
        title = LocalizedField(required=True)
  • Make all languages optional:

    class MyModel(LocalizedModel):
        title = LocalizedField(null=True)

Uniqueness

By default the values stored in a LocalizedField are not unique. You can enforce uniqueness for certain languages. This uniqueness constraint is enforced on a database level using a UNIQUE INDEX.

  • Enforce uniqueness for one or more languages:

    class MyModel(LocalizedModel):
        title = LocalizedField(uniqueness=['en', 'ro'])
  • Enforce uniqueness for all languages:

    from localized_fields import get_language_codes
    
    class MyModel(LocalizedModel):
        title = LocalizedField(uniqueness=get_language_codes())
  • Enforce uniqueness for one ore more languages together (similar to Django’s unique_together):

    class MyModel(LocalizedModel):
        title = LocalizedField(uniqueness=[('en', 'ro')])
  • Enforce uniqueness for all languages together:

    from localized_fields import get_language_codes
    
    class MyModel(LocalizedModel):
        title = LocalizedField(uniqueness=[(*get_language_codes())])

Other fields

Besides LocalizedField, there’s also:

  • LocalizedUniqueSlugField

    Successor of LocalizedAutoSlugField that fixes concurrency issues and enforces uniqueness of slugs on a database level. Usage is the exact same:

    from localized_fields import (LocalizedModel,
                                  AtomicSlugRetryMixin,
                                  LocalizedField,
                                  LocalizedUniqueSlugField)
    
    class MyModel(AtomicSlugRetryMixin, LocalizedModel):
         title = LocalizedField()
         slug = LocalizedUniqueSlugField(populate_from='title')

    By setting the option include_time=True

    slug = LocalizedUniqueSlugField(populate_from='title', include_time=True)

    You can instruct the field to include a part of the current time into the resulting slug. This is useful if you’re running into a lot of collisions.

  • LocalizedAutoSlugField

    Automatically creates a slug for every language from the specified field.

    Currently only supports populate_from. Example usage:

    from localized_fields import (LocalizedModel,
                                  LocalizedField,
                                  LocalizedUniqueSlugField)
    
    class MyModel(LocalizedModel):
         title = LocalizedField()
         slug = LocalizedAutoSlugField(populate_from='title')

    This implementation is NOT concurrency safe, prefer LocalizedUniqueSlugField.

  • LocalizedBleachField
    Automatically bleaches the content of the field.
    • django-bleach

    Example usage:

    from localized_fields import (LocalizedModel,
                                  LocalizedField,
                                  LocalizedBleachField)
    
    class MyModel(LocalizedModel):
         title = LocalizedField()
         description = LocalizedBleachField()

Experimental feature

Enables the following experimental features:
  • LocalizedField will return None instead of an empty LocalizedValue if there is no database value.

LOCALIZED_FIELDS_EXPERIMENTAL = True

Frequently asked questions (FAQ)

  1. Does this package work with Python 2?

    No. Only Python 3.5 or newer is supported. We’re using type hints. These do not work well under older versions of Python.

  2. Does this package work with Django 1.X?

    No. Only Django 1.10 or newer is supported. This is because we rely on Django’s HStoreField.

  3. Does this package come with support for Django Admin?

    Yes. Our custom fields come with a special form that will automatically be used in Django Admin if the field is of LocalizedField.

  4. Why should I pick this over any of the other translation packages out there?

    You should pick whatever you feel comfortable with. This package stores translations in your database without having to have translation tables. It however only works on PostgreSQL.

  5. I am using PostgreSQL <9.6, can I use this?

    No. The hstore data type was introduced in PostgreSQL 9.6.

  6. I am using this package. Can I give you some beer?

    Yes! If you’re ever in the area of Cluj-Napoca, Romania, swing by :)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django-localized-fields-3.6.tar.gz (18.1 kB view hashes)

Uploaded Source

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page