mirror of
https://github.com/SectorLabs/django-localized-fields.git
synced 2025-04-24 19:32:53 +03:00
Replace README with Markdown version
This commit is contained in:
parent
3bf7926c57
commit
e6ce2da161
59
README.md
Normal file
59
README.md
Normal file
@ -0,0 +1,59 @@
|
||||
| | | |
|
||||
|--------------------|---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| :white_check_mark: | **Tests** | [](https://circleci.com/gh/SectorLabs/django-localized-fields/tree/master) |
|
||||
| :memo: | **License** | [](http://doge.mit-license.org) |
|
||||
| :package: | **PyPi** | [](https://pypi.python.org/pypi/django-localized-fields) |
|
||||
| <img src="https://icon-library.net/images/django-icon/django-icon-0.jpg" width="22px" height="22px" align="center" /> | **Django Versions** | 2.0, 2.1, 2.2 |
|
||||
| <img src="http://www.iconarchive.com/download/i73027/cornmanthe3rd/plex/Other-python.ico" width="22px" height="22px" align="center" /> | **Python Versions** | 3.7, 3.8 |
|
||||
| :book: | **Documentation** | [Read The Docs](https://django-localized-fields.readthedocs.io) |
|
||||
| :warning: | **Upgrade** | [Upgrade fom v5.x](https://django-localized-fields.readthedocs.io/releases)
|
||||
| :checkered_flag: | **Installation** | [Installation Guide](https://django-localized-fields.readthedocs.io/#installation) |
|
||||
|
||||
`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` since Django 1.10.
|
||||
|
||||
---
|
||||
|
||||
:warning: **This README is for v6 which is currently under development. See the `v5.x` branch for v5.x.**
|
||||
|
||||
---
|
||||
|
||||
## Working with the code
|
||||
### Prerequisites
|
||||
|
||||
* PostgreSQL 10 or newer.
|
||||
* Django 2.0 or newer.
|
||||
* Python 3.7 or newer.
|
||||
|
||||
### Getting started
|
||||
|
||||
1. Clone the repository:
|
||||
|
||||
λ git clone https://github.com/SectorLabs/django-localized-fields.git
|
||||
|
||||
2. Create a virtual environment:
|
||||
|
||||
λ cd django-localized-fields
|
||||
λ virtualenv env
|
||||
λ source env/bin/activate
|
||||
|
||||
3. Create a postgres user for use in tests (skip if your default user is a postgres superuser):
|
||||
|
||||
λ createuser --superuser psqlextra --pwprompt
|
||||
λ export DATABASE_URL=postgres://psqlextra:<password>@localhost/psqlextra
|
||||
|
||||
Hint: if you're using virtualenvwrapper, you might find it beneficial to put
|
||||
the ``export`` line in ``$VIRTUAL_ENV/bin/postactivate`` so that it's always
|
||||
available when using this virtualenv.
|
||||
|
||||
4. Install the development/test dependencies:
|
||||
|
||||
λ pip install -r requirements/test.txt
|
||||
λ pip install -r requirements/analysis.txt
|
||||
|
||||
5. Run the tests:
|
||||
|
||||
λ tox
|
||||
|
||||
7. Auto-format code, sort imports and auto-fix linting errors:
|
||||
|
||||
λ python setup.py fix
|
408
README.rst
408
README.rst
@ -1,408 +0,0 @@
|
||||
django-localized-fields
|
||||
=======================
|
||||
|
||||
.. image:: https://circleci.com/gh/SectorLabs/django-localized-fields.svg?style=svg
|
||||
:target: https://circleci.com/gh/SectorLabs/django-localized-fields
|
||||
|
||||
.. image:: https://img.shields.io/github/license/SectorLabs/django-localized-fields.svg
|
||||
:target: https://github.com/SectorLabs/django-localized-fields/blob/master/LICENSE.md
|
||||
|
||||
.. 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`` since Django 1.10.
|
||||
|
||||
This package requires Python 3.7 or newer, Django 2.0 or newer and PostgreSQL 10 or newer.
|
||||
|
||||
Contributors
|
||||
------------
|
||||
|
||||
* `seroy <https://github.com/seroy/>`_
|
||||
* `umazalakain <https://github.com/umazalakain/>`_
|
||||
|
||||
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')
|
||||
)
|
||||
|
||||
|
||||
4. Apply migrations to enable the HStore extension:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python manage.py migrate
|
||||
|
||||
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
|
||||
print(new.title.get('ar', 'haha')) # prints 'haha' if there is no value in arabic
|
||||
|
||||
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**
|
||||
|
||||
Constraints are enforced on a database level.
|
||||
|
||||
* Optional filling
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MyModel(models.Model):
|
||||
title = LocalizedField(blank=True, null=True, required=False)
|
||||
|
||||
* Make translation required for any language
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MyModel(models.Model):
|
||||
title = LocalizedField(blank=False, null=False, required=False)
|
||||
|
||||
* Make translation required for specific languages
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MyModel(models.Model):
|
||||
title = LocalizedField(blank=False, null=False, required=['en', 'ro'])
|
||||
|
||||
* Make translation required for all languages
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MyModel(models.Model):
|
||||
title = LocalizedField(blank=False, null=False, required=True)
|
||||
|
||||
* By default the primary language **required** and the others optional:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MyModel(models.Model):
|
||||
title = LocalizedField()
|
||||
|
||||
**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')
|
||||
|
||||
``populate_from`` can be:
|
||||
|
||||
- The name of a field.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
slug = LocalizedUniqueSlugField(populate_from='name', include_time=True)
|
||||
|
||||
- A callable.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def generate_slug(instance):
|
||||
return instance.title
|
||||
|
||||
slug = LocalizedUniqueSlugField(populate_from=generate_slug, include_time=True)
|
||||
|
||||
- A tuple of names of fields.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
slug = LocalizedUniqueSlugField(populate_from=('name', 'beer') include_time=True)
|
||||
|
||||
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.
|
||||
|
||||
* ``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()
|
||||
|
||||
* ``LocalizedIntegerField``
|
||||
This is an experimental field type introduced in version 5.0 and is subject to change. It also has
|
||||
some pretty major downsides due to the fact that values are stored as strings and are converted
|
||||
back and forth.
|
||||
|
||||
Allows storing integers in multiple languages. This works exactly like ``LocalizedField`` except that
|
||||
all values must be integers. Do note that values are stored as strings in your database because
|
||||
the backing field type is ``hstore``, which only allows storing strings. The ``LocalizedIntegerField``
|
||||
takes care of ensuring that all values are integers and converts the stored strings back to integers
|
||||
when retrieving them from the database. Do not expect to be able to do queries such as:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
MyModel.objects.filter(score__en__gt=1)
|
||||
|
||||
|
||||
* ``LocalizedCharField`` and ``LocalizedTextField``
|
||||
These 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.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.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.
|
||||
* ``LocalizedField`` lookups will lookup by currently active language instead of HStoreField
|
||||
|
||||
.. 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)
|
||||
|
||||
|
||||
.. image:: ./images/admin-widget.png
|
||||
:alt: The appearance of admin widget
|
||||
|
||||
|
||||
Frequently asked questions (FAQ)
|
||||
--------------------------------
|
||||
|
||||
1. Does this package work with Python 2?
|
||||
|
||||
No. Only Python 3.7 or newer is supported. We're using type hints. These do not work well under older versions of Python.
|
||||
|
||||
2. With what Django versions does this package work?
|
||||
|
||||
Only Django 2.x or newer is supported. This is because we rely on Django's ``HStoreField`` and template-based widget rendering.
|
||||
|
||||
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 <10, 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 :)
|
Loading…
x
Reference in New Issue
Block a user