diff --git a/README.rst b/README.rst index 4dde888..6f15f91 100644 --- a/README.rst +++ b/README.rst @@ -61,15 +61,15 @@ Usage Preparation ^^^^^^^^^^^ -Declare fields on your model as ``LocalizedField``: +Inherit your model from ``LocalizedModel`` and declare fields on your model as ``LocalizedField``: .. code-block:: python - from django.db import models + from localized_fields.models import LocalizedModel from localized_fields.fields import LocalizedField - class MyModel(models.Model): + class MyModel(LocalizedModel): title = LocalizedField() @@ -160,11 +160,11 @@ Besides ``LocalizedField``, there's also: .. code-block:: python - from django.db import models + from localized_fields.models import LocalizedModel from localized_fields.fields import (LocalizedField, LocalizedAutoSlugField) - class MyModel(models.Model): + class MyModel(LocalizedModel): title = LocalizedField() slug = LocalizedAutoSlugField(populate_from='title') @@ -176,10 +176,10 @@ Besides ``LocalizedField``, there's also: .. code-block:: python - from django.db import models + from localized_fields.models import LocalizedModel from localized_fields.fields import (LocalizedField, LocalizedBleachField) - class MyModel(models.Model): + class MyModel(LocalizedModel): title = LocalizedField() description = LocalizedBleachField() diff --git a/localized_fields/fields/localized_autoslug_field.py b/localized_fields/fields/localized_autoslug_field.py index dc19c91..eff17c8 100644 --- a/localized_fields/fields/localized_autoslug_field.py +++ b/localized_fields/fields/localized_autoslug_field.py @@ -1,8 +1,10 @@ from typing import Callable +from django import forms from django.conf import settings from django.utils.text import slugify +from ..forms import LocalizedFieldForm from .localized_field import LocalizedField from .localized_value import LocalizedValue @@ -28,6 +30,24 @@ class LocalizedAutoSlugField(LocalizedField): return name, path, args, kwargs + def formfield(self, **kwargs): + """Gets the form field associated with this field. + + Because this is a slug field which is automatically + populated, it should be hidden from the form. + """ + + defaults = { + 'form_class': LocalizedFieldForm + } + + defaults.update(kwargs) + + form_field = super().formfield(**defaults) + form_field.widget = forms.HiddenInput() + + return form_field + def pre_save(self, instance, add: bool): """Ran just before the model is saved, allows us to built the slug. diff --git a/localized_fields/fields/localized_field.py b/localized_fields/fields/localized_field.py index 1e04c41..156e631 100644 --- a/localized_fields/fields/localized_field.py +++ b/localized_fields/fields/localized_field.py @@ -17,9 +17,6 @@ class LocalizedField(HStoreField): def __init__(self, *args, **kwargs): """Initializes a new instance of :see:LocalizedValue.""" - if 'default' not in kwargs: - kwargs['default'] = LocalizedValue() - super(LocalizedField, self).__init__(*args, **kwargs) @staticmethod diff --git a/localized_fields/models.py b/localized_fields/models.py new file mode 100644 index 0000000..5ab1b1b --- /dev/null +++ b/localized_fields/models.py @@ -0,0 +1,29 @@ +from django.db import models + +from .fields import LocalizedField, LocalizedValue + + +class LocalizedModel(models.Model): + """A model that contains localized fields.""" + + class Meta: + abstract = True + + def __init__(self, *args, **kwargs): + """Initializes a new instance of :see:LocalizedModel. + + Here we set all the fields that are of :see:LocalizedField + to an instance of :see:LocalizedValue in case they are none + so that the user doesn't explicitely have to do so.""" + + super(LocalizedModel, self).__init__(*args, **kwargs) + + for field in self._meta.get_fields(): + if not isinstance(field, LocalizedField): + continue + + value = getattr(self, field.name, None) + if not isinstance(value, LocalizedValue): + value = LocalizedValue() + + setattr(self, field.name, value) diff --git a/tests/fake_model.py b/tests/fake_model.py new file mode 100644 index 0000000..02e4cf6 --- /dev/null +++ b/tests/fake_model.py @@ -0,0 +1,47 @@ +from django.contrib.postgres.operations import HStoreExtension +from django.db import connection, migrations +from django.db.migrations.executor import MigrationExecutor + +from localized_fields.fields import LocalizedAutoSlugField, LocalizedField +from localized_fields.models import LocalizedModel + +MODEL = None + + +def get_fake_model(): + """Creates a fake model to use during unit tests.""" + + global MODEL + + if MODEL: + return MODEL + + class TestModel(LocalizedModel): + """Model used for testing the :see:LocalizedAutoSlugField.""" + + app_label = 'localized_fields' + + title = LocalizedField() + slug = LocalizedAutoSlugField(populate_from='title') + + class TestProject: + + def clone(self, *args, **kwargs): + return self + + class TestMigration(migrations.Migration): + operations = [ + HStoreExtension() + ] + + with connection.schema_editor() as schema_editor: + migration_executor = MigrationExecutor(schema_editor.connection) + migration_executor.apply_migration( + TestProject(), + TestMigration('eh', 'localized_fields') + ) + + schema_editor.create_model(TestModel) + + MODEL = TestModel + return MODEL diff --git a/tests/test_localized_auto_slug_field.py b/tests/test_localized_auto_slug_field.py index 3a05078..5162e1b 100644 --- a/tests/test_localized_auto_slug_field.py +++ b/tests/test_localized_auto_slug_field.py @@ -1,12 +1,10 @@ from django.conf import settings -from django.contrib.postgres.operations import HStoreExtension -from django.db import connection, migrations, models -from django.db.migrations.executor import MigrationExecutor from django.test import TestCase from django.utils.text import slugify -from localized_fields.fields import (LocalizedAutoSlugField, LocalizedField, - LocalizedValue) +from localized_fields.fields import LocalizedAutoSlugField + +from .fake_model import get_fake_model class LocalizedAutoSlugFieldTestCase(TestCase): @@ -20,39 +18,7 @@ class LocalizedAutoSlugFieldTestCase(TestCase): super(LocalizedAutoSlugFieldTestCase, cls).setUpClass() - class TestModel(models.Model): - """Model used for testing the :see:LocalizedAutoSlugField.""" - - app_label = 'localized_fields' - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self.title = self.title or LocalizedValue() - self.slug = self.slug or LocalizedValue() - - title = LocalizedField() - slug = LocalizedAutoSlugField(populate_from='title') - - class TestProject: - - def clone(self, *args, **kwargs): - return self - - class TestMigration(migrations.Migration): - operations = [ - HStoreExtension() - ] - - with connection.schema_editor() as schema_editor: - migration_executor = MigrationExecutor(schema_editor.connection) - migration_executor.apply_migration( - TestProject(), - TestMigration('eh', 'localized_fields') - ) - schema_editor.create_model(TestModel) - - cls.TestModel = TestModel + cls.TestModel = get_fake_model() def test_populate(self): """Tests whether the :see:LocalizedAutoSlugField's diff --git a/tests/test_localized_field.py b/tests/test_localized_field.py index 89585b6..a1c972d 100644 --- a/tests/test_localized_field.py +++ b/tests/test_localized_field.py @@ -235,33 +235,6 @@ class LocalizedFieldTestCase(TestCase): assert not LocalizedField().clean(None) assert not LocalizedField().clean(['huh']) - @staticmethod - def test_default_value(): - """Tests whether the default value is a :see:LocalizedValue - instance.""" - - field = LocalizedField() - - assert field.default - assert isinstance(field.default, LocalizedValue) - - for lang_code, _ in settings.LANGUAGES: - assert not field.default.get(lang_code) - - @staticmethod - def test_default_value_override(): - """Tests whether the default value of a field - can correctly be overriden.""" - - default_value = LocalizedValue(get_init_values()) - field = LocalizedField(default=default_value) - - assert field.default - assert isinstance(field.default, LocalizedValue) - - for lang_code, _ in settings.LANGUAGES: - assert default_value.get(lang_code) == field.default.get(lang_code) - @staticmethod def test_formfield(): """Tests whether the :see:formfield function diff --git a/tests/test_localized_model.py b/tests/test_localized_model.py new file mode 100644 index 0000000..6cd2dca --- /dev/null +++ b/tests/test_localized_model.py @@ -0,0 +1,29 @@ +from django.test import TestCase + +from localized_fields.fields import LocalizedValue + +from .fake_model import get_fake_model + + +class LocalizedModelTestCase(TestCase): + """Tests whether the :see:LocalizedModel class.""" + + TestModel = None + + @classmethod + def setUpClass(cls): + """Creates the test model in the database.""" + + super(LocalizedModelTestCase, cls).setUpClass() + + cls.TestModel = get_fake_model() + + @classmethod + def test_defaults(cls): + """Tests whether all :see:LocalizedField + fields are assigned an empty :see:LocalizedValue + instance when the model is instanitiated.""" + + obj = cls.TestModel() + + assert isinstance(obj.title, LocalizedValue)