diff --git a/localized_fields/db_backend/base.py b/localized_fields/db_backend/base.py index cf74fab..6707df3 100644 --- a/localized_fields/db_backend/base.py +++ b/localized_fields/db_backend/base.py @@ -5,6 +5,8 @@ from django.core.exceptions import ImproperlyConfigured from django.db.backends.postgresql.base import \ DatabaseWrapper as Psycopg2DatabaseWrapper +from ..fields import LocalizedField + def _get_backend_base(): """Gets the base class for the custom database back-end. @@ -153,7 +155,22 @@ class SchemaEditor(_get_schema_editor_base()): *args, **kwargs ) - self._update_hstore_constraints(model, old_field, new_field) + is_old_field_localized = isinstance(old_field, LocalizedField) + is_new_field_localized = isinstance(new_field, LocalizedField) + + if is_old_field_localized or is_new_field_localized: + self._update_hstore_constraints(model, old_field, new_field) + + def create_model(self, model): + """Ran when a new model is created.""" + + super().create_model(model) + + for field in model._meta.local_fields: + if not isinstance(field, LocalizedField): + continue + + self._update_hstore_constraints(model, field, field) class DatabaseWrapper(_get_backend_base()): diff --git a/localized_fields/fields/localized_magicslug_field.py b/localized_fields/fields/localized_magicslug_field.py index 0140e01..50d7a34 100644 --- a/localized_fields/fields/localized_magicslug_field.py +++ b/localized_fields/fields/localized_magicslug_field.py @@ -59,6 +59,9 @@ class LocalizedMagicSlugField(LocalizedAutoSlugField): continue slug = slugify(value, allow_unicode=True) + if instance.retries > 0: + slug += '-%d' % instance.retries + slugs.set(lang_code, slug) setattr(instance, self.name, slugs) diff --git a/localized_fields/models.py b/localized_fields/models.py index 5b2061c..72d0299 100644 --- a/localized_fields/models.py +++ b/localized_fields/models.py @@ -1,4 +1,6 @@ from django.db import models +from django.db.utils import IntegrityError +from django.db import transaction from .fields import LocalizedField from .localized_value import LocalizedValue @@ -32,3 +34,30 @@ class LocalizedModel(models.Model): value = LocalizedValue() setattr(self, field.name, value) + + def save(self, *args, **kwargs): + """Saves this model instance to the database.""" + + if not hasattr(self, 'retries'): + self.retries = 0 + + error = None + with transaction.atomic(): + try: + return super(LocalizedModel, self).save(*args, **kwargs) + except IntegrityError as ex: + # this is as retarded as it looks, there's no + # way we can put the retry logic inside the slug + # field class... we can also not only catch exceptions + # that apply to slug fields... so yea.. this is as + # retarded as it gets... i am sorry :( + if 'slug' not in str(ex): + raise ex + + error = ex + + if self.retries >= 100: + raise error + + self.retries += 1 + return self.save() diff --git a/settings.py b/settings.py index 49ef880..1d29ae8 100644 --- a/settings.py +++ b/settings.py @@ -3,7 +3,7 @@ import dj_database_url DEBUG = True TEMPLATE_DEBUG = True -SECRET_KEY = 'this is my secret key' +SECRET_KEY = 'this is my secret key' # NOQA TEST_RUNNER = 'django.test.runner.DiscoverRunner' @@ -11,6 +11,8 @@ DATABASES = { 'default': dj_database_url.config(default='postgres:///localized_fields') } +DATABASES['default']['ENGINE'] = 'localized_fields.db_backend' + LANGUAGE_CODE = 'en' LANGUAGES = ( ('en', 'English'), diff --git a/tests/fake_model.py b/tests/fake_model.py index 383af97..7005a13 100644 --- a/tests/fake_model.py +++ b/tests/fake_model.py @@ -1,10 +1,7 @@ -from django.contrib.postgres.operations import HStoreExtension from django.db import connection, migrations +from localized_fields import LocalizedModel from django.db.migrations.executor import MigrationExecutor -import sys - -from localized_fields import (LocalizedAutoSlugField, LocalizedField, - LocalizedModel, LocalizedMagicSlugField) +from django.contrib.postgres.operations import HStoreExtension def get_fake_model(name='TestModel', fields={}): diff --git a/tests/test_localized_slug_fields.py b/tests/test_localized_slug_fields.py index 6b13f21..90a0001 100644 --- a/tests/test_localized_slug_fields.py +++ b/tests/test_localized_slug_fields.py @@ -1,10 +1,10 @@ from django import forms from django.conf import settings from django.test import TestCase +from localized_fields import (LocalizedField, LocalizedAutoSlugField, + LocalizedMagicSlugField) from django.utils.text import slugify -from localized_fields import LocalizedField, LocalizedAutoSlugField, LocalizedMagicSlugField - from .fake_model import get_fake_model @@ -71,7 +71,6 @@ class LocalizedSlugFieldTestCase(TestCase): @classmethod def test_deconstruct_auto(cls): cls._test_deconstruct(LocalizedAutoSlugField) - cls._test_deconstruct(LocalizedMagicSlugField) @classmethod def test_deconstruct_magic(cls): @@ -93,7 +92,7 @@ class LocalizedSlugFieldTestCase(TestCase): obj.title.en = 'this is my title' obj.save() - assert obj.slug.get('en') == slugify(obj.title.en) + assert obj.slug.get('en') == slugify(obj.title) @staticmethod def _test_populate_multiple_languages(model): @@ -117,11 +116,12 @@ class LocalizedSlugFieldTestCase(TestCase): obj.title.en = 'title' obj.save() - another_obj = model() - another_obj.title.en = 'title' - another_obj.save() + for i in range(1, 90): + another_obj = model() + another_obj.title.en = 'title' + another_obj.save() - assert another_obj.slug.en == 'title-1' + assert another_obj.slug.en == 'title-%d' % i @staticmethod def _test_unique_slug_utf(model):