From e6ce2da1613572fcda235c9ecd696d5362639e98 Mon Sep 17 00:00:00 2001 From: Swen Kooij Date: Sun, 20 Oct 2019 17:43:41 +0300 Subject: [PATCH] Replace README with Markdown version --- README.md | 59 ++++++++ README.rst | 408 ----------------------------------------------------- setup.py | 2 +- 3 files changed, 60 insertions(+), 409 deletions(-) create mode 100644 README.md delete mode 100644 README.rst diff --git a/README.md b/README.md new file mode 100644 index 0000000..6b0398d --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +| | | | +|--------------------|---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| :white_check_mark: | **Tests** | [![CircleCI](https://circleci.com/gh/SectorLabs/django-localized-fields/tree/master.svg?style=svg)](https://circleci.com/gh/SectorLabs/django-localized-fields/tree/master) | +| :memo: | **License** | [![License](https://img.shields.io/:license-mit-blue.svg)](http://doge.mit-license.org) | +| :package: | **PyPi** | [![PyPi](https://badge.fury.io/py/django-localized-fields.svg)](https://pypi.python.org/pypi/django-localized-fields) | +| | **Django Versions** | 2.0, 2.1, 2.2 | +| | **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:@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 diff --git a/README.rst b/README.rst deleted file mode 100644 index aa3e629..0000000 --- a/README.rst +++ /dev/null @@ -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 `_ -* `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`` (````) 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//_ - 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 :) diff --git a/setup.py b/setup.py index 7d30040..89ac527 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ def create_command(text, commands): with open( - os.path.join(os.path.dirname(__file__), "README.rst"), encoding="utf-8" + os.path.join(os.path.dirname(__file__), "README.md"), encoding="utf-8" ) as readme: README = readme.read()