mirror of
https://github.com/SectorLabs/django-localized-fields.git
synced 2025-04-25 03:32:55 +03:00
348 lines
11 KiB
ReStructuredText
348 lines
11 KiB
ReStructuredText
django-localized-fields
|
|
=======================
|
|
|
|
.. image:: https://scrutinizer-ci.com/g/SectorLabs/django-localized-fields/badges/quality-score.png
|
|
:target: https://scrutinizer-ci.com/g/SectorLabs/django-localized-fields/
|
|
|
|
.. image:: https://scrutinizer-ci.com/g/SectorLabs/django-localized-fields/badges/coverage.png
|
|
:target: https://scrutinizer-ci.com/g/SectorLabs/django-localized-fields/
|
|
|
|
.. image:: https://img.shields.io/github/license/SectorLabs/django-localized-fields.svg
|
|
|
|
.. image:: https://badge.fury.io/py/django-localized-fields.svg
|
|
:target: https://pypi.python.org/pypi/django-localized-fields
|
|
|
|
``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:
|
|
|
|
.. code-block:: bash
|
|
|
|
$ pip install django-localized-fields
|
|
|
|
2. Add ``localized_fields`` and ``django.contrib.postgres`` to your ``INSTALLED_APPS``:
|
|
|
|
.. code-block:: bash
|
|
|
|
INSTALLED_APPS = [
|
|
....
|
|
|
|
'django.contrib.postgres',
|
|
'localized_fields.apps.LocalizedFieldsConfig'
|
|
]
|
|
|
|
3. Set the database engine to ``psqlextra.backend``:
|
|
|
|
.. code-block:: python
|
|
|
|
DATABASES = {
|
|
'default': {
|
|
...
|
|
'ENGINE': 'psqlextra.backend'
|
|
}
|
|
}
|
|
|
|
3. Set ``LANGUAGES` and `LANGUAGE_CODE`` in your settings:
|
|
|
|
.. code-block:: python
|
|
|
|
LANGUAGE_CODE = 'en' # default language
|
|
LANGUAGES = (
|
|
('en', 'English'),
|
|
('nl', 'Dutch'),
|
|
('ro', 'Romanian')
|
|
)
|
|
|
|
Usage
|
|
-----
|
|
|
|
Preparation
|
|
^^^^^^^^^^^
|
|
Declare fields on your model as ``LocalizedField``:
|
|
|
|
.. code-block:: python
|
|
|
|
from localized_fields.fields import LocalizedField
|
|
|
|
|
|
class MyModel(models.Model):
|
|
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:
|
|
|
|
.. code-block:: python
|
|
|
|
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
|
|
^^^^^^^^^^^
|
|
.. code-block:: python
|
|
|
|
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:
|
|
|
|
.. code-block:: python
|
|
|
|
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:
|
|
|
|
.. code-block:: python
|
|
|
|
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:
|
|
|
|
.. code-block:: python
|
|
|
|
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**):
|
|
|
|
.. code-block:: python
|
|
|
|
class MyModel(models.Model):
|
|
title = LocalizedField(required=True)
|
|
|
|
* Make all languages optional:
|
|
|
|
.. code-block:: python
|
|
|
|
class MyModel(models.Model):
|
|
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:
|
|
|
|
.. code-block:: python
|
|
|
|
class MyModel(models.Model):
|
|
title = LocalizedField(uniqueness=['en', 'ro'])
|
|
|
|
* Enforce uniqueness for **all** languages:
|
|
|
|
.. code-block:: python
|
|
|
|
from localized_fields.util import get_language_codes
|
|
|
|
class MyModel(models.Model):
|
|
title = LocalizedField(uniqueness=get_language_codes())
|
|
|
|
* Enforce uniqueness for one ore more languages **together** (similar to Django's ``unique_together``):
|
|
|
|
.. code-block:: python
|
|
|
|
class MyModel(models.Model):
|
|
title = LocalizedField(uniqueness=[('en', 'ro')])
|
|
|
|
* Enforce uniqueness for **all** languages **together**:
|
|
|
|
.. code-block:: python
|
|
|
|
from localized_fields.util import get_language_codes
|
|
|
|
class MyModel(models.Model):
|
|
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:
|
|
|
|
.. code-block:: python
|
|
|
|
from localized_fields.models import LocalizedModel
|
|
from localized_fields.fields import LocalizedField, LocalizedUniqueSlugField
|
|
|
|
class MyModel(LocalizedModel):
|
|
title = LocalizedField()
|
|
slug = LocalizedUniqueSlugField(populate_from='title')
|
|
|
|
By setting the option ``include_time=True``
|
|
|
|
.. code-block:: python
|
|
|
|
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:
|
|
|
|
.. code-block:: python
|
|
|
|
from localized_fields.fields import LocalizedField, LocalizedAutoSlugField
|
|
|
|
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:
|
|
|
|
.. code-block:: python
|
|
|
|
from localized_fields.fields import LocalizedField, LocalizedBleachField
|
|
|
|
class MyModel(models.Model):
|
|
title = LocalizedField()
|
|
description = LocalizedBleachField()
|
|
|
|
* ``LocalizedCharField`` and ``LocalizedTextField``
|
|
This fields following the Django convention for string-based fields use the empty string as value for “no data”, not NULL.
|
|
``LocalizedCharField`` uses ``TextInput`` (``<input type="text">``) widget for render.
|
|
|
|
Example usage:
|
|
|
|
.. code-block:: python
|
|
|
|
from localized_fields import (LocalizedCharField,
|
|
LocalizedTextField)
|
|
|
|
class MyModel(models.Model):
|
|
title = LocalizedCharField()
|
|
description = LocalizedTextField()
|
|
|
|
* ``LocalizedFileField``
|
|
A file-upload field
|
|
|
|
Parameter ``upload_to`` supports ``lang`` parameter for string formatting or as function argument (in case if ``upload_to`` is callable).
|
|
|
|
Example usage:
|
|
|
|
.. code-block:: python
|
|
|
|
from localized_fields import LocalizedFileField
|
|
|
|
def my_directory_path(instance, filename, lang):
|
|
# file will be uploaded to MEDIA_ROOT/<lang>/<id>_<filename>
|
|
return '{0}/{0}_{1}'.format(lang, instance.id, filename)
|
|
|
|
class MyModel(models.Model):
|
|
file1 = LocalizedFileField(upload_to='uploads/{lang}/')
|
|
file2 = LocalizedFileField(upload_to=my_directory_path)
|
|
|
|
In template you can access to file attributes:
|
|
|
|
.. code-block:: django
|
|
|
|
{# For current active language: #}
|
|
|
|
{{ model.file.url }} {# output file url #}
|
|
{{ model.file.name }} {# output file name #}
|
|
|
|
{# Or get it in a specific language: #}
|
|
|
|
{{ model.file.ro.url }} {# output file url for romanian language #}
|
|
{{ model.file.ro.name }} {# output file name for romanian language #}
|
|
|
|
To get access to file instance for current active language use ``localized`` method:
|
|
|
|
.. code-block:: python
|
|
|
|
model.file.localized()
|
|
|
|
Experimental feature
|
|
^^^^^^^^^^^^^^^^^^^^
|
|
Enables the following experimental features:
|
|
* ``LocalizedField`` will return ``None`` instead of an empty ``LocalizedValue`` if there is no database value.
|
|
|
|
.. code-block:: python
|
|
|
|
LOCALIZED_FIELDS_EXPERIMENTAL = True
|
|
|
|
|
|
Django Admin Integration
|
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
|
To enable widgets in the admin, you need to inherit from ``LocalizedFieldsAdminMixin``:
|
|
|
|
.. code-block:: python
|
|
|
|
from django.contrib import admin
|
|
from myapp.models import MyLocalizedModel
|
|
|
|
from localized_fields.admin import LocalizedFieldsAdminMixin
|
|
|
|
class MyLocalizedModelAdmin(LocalizedFieldsAdminMixin, admin.ModelAdmin):
|
|
"""Any admin options you need go here"""
|
|
|
|
admin.site.register(MyLocalizedModel, MyLocalizedModelAdmin)
|
|
|
|
|
|
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 :)
|