mirror of
https://github.com/SectorLabs/django-localized-fields.git
synced 2025-09-13 06:53:18 +03:00
Created abstract model to take care of default values
This commit is contained in:
parent
cdaacdfac5
commit
c081b0431d
14
README.rst
14
README.rst
@ -61,15 +61,15 @@ Usage
|
|||||||
|
|
||||||
Preparation
|
Preparation
|
||||||
^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
Declare fields on your model as ``LocalizedField``:
|
Inherit your model from ``LocalizedModel`` and declare fields on your model as ``LocalizedField``:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
from django.db import models
|
from localized_fields.models import LocalizedModel
|
||||||
from localized_fields.fields import LocalizedField
|
from localized_fields.fields import LocalizedField
|
||||||
|
|
||||||
|
|
||||||
class MyModel(models.Model):
|
class MyModel(LocalizedModel):
|
||||||
title = LocalizedField()
|
title = LocalizedField()
|
||||||
|
|
||||||
|
|
||||||
@ -160,11 +160,11 @@ Besides ``LocalizedField``, there's also:
|
|||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
from django.db import models
|
from localized_fields.models import LocalizedModel
|
||||||
from localized_fields.fields import (LocalizedField,
|
from localized_fields.fields import (LocalizedField,
|
||||||
LocalizedAutoSlugField)
|
LocalizedAutoSlugField)
|
||||||
|
|
||||||
class MyModel(models.Model):
|
class MyModel(LocalizedModel):
|
||||||
title = LocalizedField()
|
title = LocalizedField()
|
||||||
slug = LocalizedAutoSlugField(populate_from='title')
|
slug = LocalizedAutoSlugField(populate_from='title')
|
||||||
|
|
||||||
@ -176,10 +176,10 @@ Besides ``LocalizedField``, there's also:
|
|||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
from django.db import models
|
from localized_fields.models import LocalizedModel
|
||||||
from localized_fields.fields import (LocalizedField,
|
from localized_fields.fields import (LocalizedField,
|
||||||
LocalizedBleachField)
|
LocalizedBleachField)
|
||||||
|
|
||||||
class MyModel(models.Model):
|
class MyModel(LocalizedModel):
|
||||||
title = LocalizedField()
|
title = LocalizedField()
|
||||||
description = LocalizedBleachField()
|
description = LocalizedBleachField()
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
|
from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
|
|
||||||
|
from ..forms import LocalizedFieldForm
|
||||||
from .localized_field import LocalizedField
|
from .localized_field import LocalizedField
|
||||||
from .localized_value import LocalizedValue
|
from .localized_value import LocalizedValue
|
||||||
|
|
||||||
@ -28,6 +30,24 @@ class LocalizedAutoSlugField(LocalizedField):
|
|||||||
|
|
||||||
return name, path, args, kwargs
|
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):
|
def pre_save(self, instance, add: bool):
|
||||||
"""Ran just before the model is saved, allows us to built
|
"""Ran just before the model is saved, allows us to built
|
||||||
the slug.
|
the slug.
|
||||||
|
@ -17,9 +17,6 @@ class LocalizedField(HStoreField):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Initializes a new instance of :see:LocalizedValue."""
|
"""Initializes a new instance of :see:LocalizedValue."""
|
||||||
|
|
||||||
if 'default' not in kwargs:
|
|
||||||
kwargs['default'] = LocalizedValue()
|
|
||||||
|
|
||||||
super(LocalizedField, self).__init__(*args, **kwargs)
|
super(LocalizedField, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
29
localized_fields/models.py
Normal file
29
localized_fields/models.py
Normal file
@ -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)
|
47
tests/fake_model.py
Normal file
47
tests/fake_model.py
Normal file
@ -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
|
@ -1,12 +1,10 @@
|
|||||||
from django.conf import settings
|
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.test import TestCase
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
|
|
||||||
from localized_fields.fields import (LocalizedAutoSlugField, LocalizedField,
|
from localized_fields.fields import LocalizedAutoSlugField
|
||||||
LocalizedValue)
|
|
||||||
|
from .fake_model import get_fake_model
|
||||||
|
|
||||||
|
|
||||||
class LocalizedAutoSlugFieldTestCase(TestCase):
|
class LocalizedAutoSlugFieldTestCase(TestCase):
|
||||||
@ -20,39 +18,7 @@ class LocalizedAutoSlugFieldTestCase(TestCase):
|
|||||||
|
|
||||||
super(LocalizedAutoSlugFieldTestCase, cls).setUpClass()
|
super(LocalizedAutoSlugFieldTestCase, cls).setUpClass()
|
||||||
|
|
||||||
class TestModel(models.Model):
|
cls.TestModel = get_fake_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
|
|
||||||
|
|
||||||
def test_populate(self):
|
def test_populate(self):
|
||||||
"""Tests whether the :see:LocalizedAutoSlugField's
|
"""Tests whether the :see:LocalizedAutoSlugField's
|
||||||
|
@ -235,33 +235,6 @@ class LocalizedFieldTestCase(TestCase):
|
|||||||
assert not LocalizedField().clean(None)
|
assert not LocalizedField().clean(None)
|
||||||
assert not LocalizedField().clean(['huh'])
|
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
|
@staticmethod
|
||||||
def test_formfield():
|
def test_formfield():
|
||||||
"""Tests whether the :see:formfield function
|
"""Tests whether the :see:formfield function
|
||||||
|
29
tests/test_localized_model.py
Normal file
29
tests/test_localized_model.py
Normal file
@ -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)
|
Loading…
x
Reference in New Issue
Block a user