17 Commits
v6.1 ... v6.6

Author SHA1 Message Date
Swen Kooij
8c7d0773f7 Bump version number to v6.6 2021-11-08 15:08:48 +02:00
Swen Kooij
cc911d4909 LocalizedAutoSlugField should only warn about deprecation if used 2021-11-08 15:08:08 +02:00
Swen Kooij
0f30cc1493 Ignore build/ folder 2021-11-08 14:39:32 +02:00
Swen Kooij
0d1e9510cf Fix broken Django icon in README 2021-11-08 14:39:20 +02:00
Swen Kooij
25c1c24ccb Declare support for Django 3.2 and Python 3.9 2021-11-08 14:38:01 +02:00
Swen Kooij
bd3005a7e9 Bump version number to 6.5 2021-11-08 14:36:55 +02:00
Swen Kooij
7902d8225a Do not set default_app_config for Django 3.2 and newer
See: See: https://docs.djangoproject.com/en/3.2/releases/3.2/#what-s-new-in-django-3-2
2021-11-08 14:36:45 +02:00
Swen Kooij
f024e4feb5 Bump version to v6.4 2021-03-22 07:47:23 +02:00
Swen Kooij
92cb5e8b1f Add LocalizedValue.is_empty() 2021-03-22 07:47:00 +02:00
Swen Kooij
5c298ef13e Bump version number to 6.3 2021-03-13 14:01:51 +02:00
Swen Kooij
1b3e5989d3 LocalizedUniqueSlugField should properly deconstruct 'enabled' flag 2021-03-13 13:45:22 +02:00
Swen Kooij
d57f9a41bb Mark pytest as a third-party library for isort
Not sure why it doesn't get that.
2021-03-13 13:32:01 +02:00
Swen Kooij
bd8924224e Add flag to disable LocalizedUniqueSlugField 2021-03-13 13:24:36 +02:00
Swen Kooij
62e1e805c7 Bump version number to v6.2 2020-12-07 10:49:36 +02:00
Swen Kooij
afc39745bf Bump version number to 6.2rc1 2020-11-30 12:28:03 +02:00
Swen Kooij
1406954dec Merge pull request #91 from SectorLabs/immutable-slugs
Add a flag to make LocalizedUniqueSlugField immutable
2020-11-30 12:26:47 +02:00
Swen Kooij
afb94ecf66 Add a flag to make LocalizedUniqueSlugField immutable 2020-11-27 16:36:23 +02:00
10 changed files with 112 additions and 12 deletions

1
.gitignore vendored
View File

@@ -15,6 +15,7 @@ reports/
# Ignore build results
*.egg-info/
dist/
build/
pip-wheel-metadata
# Ignore stupid .DS_Store

View File

@@ -3,7 +3,7 @@
| :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) |
| <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, 3.0, 3.1 |
| <img src="https://cdn.iconscout.com/icon/free/png-256/django-1-282754.png" width="22px" height="22px" align="center" /> | **Django Versions** | 2.0, 2.1, 2.2, 3.0, 3.1, 3.2 |
| <img src="http://www.iconarchive.com/download/i73027/cornmanthe3rd/plex/Other-python.ico" width="22px" height="22px" align="center" /> | **Python Versions** | 3.6, 3.7, 3.8, 3.9 |
| :book: | **Documentation** | [Read The Docs](https://django-localized-fields.readthedocs.io) |
| :warning: | **Upgrade** | [Upgrade fom v5.x](https://django-localized-fields.readthedocs.io/en/latest/releases.html#v6-0)

View File

@@ -1 +1,4 @@
default_app_config = "localized_fields.apps.LocalizedFieldsConfig"
import django
if django.VERSION < (3, 2):
default_app_config = "localized_fields.apps.LocalizedFieldsConfig"

View File

@@ -16,14 +16,14 @@ from .field import LocalizedField
class LocalizedAutoSlugField(LocalizedField):
"""Automatically provides slugs for a localized field upon saving."""
warnings.warn(
"LocalizedAutoSlug is deprecated and will be removed in the next major version.",
DeprecationWarning,
)
def __init__(self, *args, **kwargs):
"""Initializes a new instance of :see:LocalizedAutoSlugField."""
warnings.warn(
"LocalizedAutoSlug is deprecated and will be removed in the next major version.",
DeprecationWarning,
)
self.populate_from = kwargs.pop("populate_from", None)
self.include_time = kwargs.pop("include_time", False)

View File

@@ -21,6 +21,10 @@ class LocalizedUniqueSlugField(LocalizedAutoSlugField):
When in doubt, use this over :see:LocalizedAutoSlugField.
Inherit from :see:AtomicSlugRetryMixin in your model to
make this field work properly.
By default, this creates a new slug if the field(s) specified
in `populate_from` are changed. Set `immutable=True` to get
immutable slugs.
"""
def __init__(self, *args, **kwargs):
@@ -28,6 +32,9 @@ class LocalizedUniqueSlugField(LocalizedAutoSlugField):
kwargs["uniqueness"] = kwargs.pop("uniqueness", get_language_codes())
self.enabled = kwargs.pop("enabled", True)
self.immutable = kwargs.pop("immutable", False)
super(LocalizedUniqueSlugField, self).__init__(*args, **kwargs)
self.populate_from = kwargs.pop("populate_from")
@@ -42,6 +49,13 @@ class LocalizedUniqueSlugField(LocalizedAutoSlugField):
kwargs["populate_from"] = self.populate_from
kwargs["include_time"] = self.include_time
if self.enabled is False:
kwargs["enabled"] = self.enabled
if self.immutable is True:
kwargs["immutable"] = self.immutable
return name, path, args, kwargs
def pre_save(self, instance, add: bool):
@@ -59,6 +73,9 @@ class LocalizedUniqueSlugField(LocalizedAutoSlugField):
The localized slug that was generated.
"""
if not self.enabled:
return getattr(instance, self.name)
if not isinstance(instance, AtomicSlugRetryMixin):
raise ImproperlyConfigured(
(
@@ -76,10 +93,14 @@ class LocalizedUniqueSlugField(LocalizedAutoSlugField):
slug = slugify(value, allow_unicode=True)
current_slug = getattr(instance, self.name).get(lang_code)
if current_slug and self.immutable:
slugs.set(lang_code, current_slug)
continue
# verify whether it's needed to re-generate a slug,
# if not, re-use the same slug
if instance.pk is not None:
current_slug = getattr(instance, self.name).get(lang_code)
if current_slug is not None:
current_slug_end_index = current_slug.rfind("-")
stripped_slug = current_slug[0:current_slug_end_index]

View File

@@ -136,6 +136,15 @@ class LocalizedValue(dict):
return None
def is_empty(self) -> bool:
"""Gets whether all the languages contain the default value."""
for lang_code, _ in settings.LANGUAGES:
if self.get(lang_code) != self.default_value:
return False
return True
def __str__(self) -> str:
"""Gets the value in the current language or falls back to the next
language if there's no value in the current language."""

View File

@@ -9,4 +9,4 @@ lines_between_types=1
include_trailing_comma=True
not_skip=__init__.py
known_standard_library=dataclasses
known_third_party=django_bleach,bleach
known_third_party=django_bleach,bleach,pytest

View File

@@ -36,7 +36,7 @@ with open(
setup(
name="django-localized-fields",
version="6.1",
version="6.6",
packages=find_packages(exclude=["tests"]),
include_package_data=True,
license="MIT License",
@@ -64,6 +64,9 @@ setup(
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Topic :: Internet :: WWW/HTTP",
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
],

View File

@@ -1,5 +1,7 @@
import copy
import pytest
from django import forms
from django.conf import settings
from django.db import models
@@ -215,6 +217,53 @@ class LocalizedSlugFieldTestCase(TestCase):
for lang_code, lang_name in settings.LANGUAGES:
assert obj.slug.get(lang_code) == "title-%s" % lang_name.lower()
@classmethod
def test_disable(cls):
"""Tests whether disabling auto-slugging works."""
Model = get_fake_model(
{
"title": LocalizedField(),
"slug": LocalizedUniqueSlugField(
populate_from="title", enabled=False
),
}
)
obj = Model()
obj.title = "test"
# should raise IntegrityError because auto-slugging
# is disabled and the slug field is NULL
with pytest.raises(IntegrityError):
obj.save()
@classmethod
def test_allows_override_when_immutable(cls):
"""Tests whether setting a value manually works and does not get
overriden."""
Model = get_fake_model(
{
"title": LocalizedField(),
"name": models.CharField(max_length=255),
"slug": LocalizedUniqueSlugField(
populate_from="title", immutable=True
),
}
)
obj = Model()
for lang_code, lang_name in settings.LANGUAGES:
obj.slug.set(lang_code, "my value %s" % lang_code)
obj.title.set(lang_code, "my title %s" % lang_code)
obj.save()
for lang_code, lang_name in settings.LANGUAGES:
assert obj.slug.get(lang_code) == "my value %s" % lang_code
@classmethod
def test_unique_slug(cls):
"""Tests whether unique slugs are properly generated."""
@@ -248,10 +297,13 @@ class LocalizedSlugFieldTestCase(TestCase):
"""Tests whether the :see:deconstruct function properly retains options
specified in the constructor."""
field = LocalizedUniqueSlugField(populate_from="title")
field = LocalizedUniqueSlugField(
enabled=False, immutable=True, populate_from="title"
)
_, _, _, kwargs = field.deconstruct()
assert "populate_from" in kwargs
assert not kwargs["enabled"]
assert kwargs["immutable"]
assert kwargs["populate_from"] == field.populate_from
@staticmethod

View File

@@ -38,6 +38,17 @@ class LocalizedValueTestCase(TestCase):
for lang_code, _ in settings.LANGUAGES:
assert getattr(value, lang_code) is None
@staticmethod
def test_is_empty():
"""Tests whether a newly constructed :see:LocalizedValue without any
content is considered "empty"."""
value = LocalizedValue()
assert value.is_empty()
value.set(settings.LANGUAGE_CODE, "my value")
assert not value.is_empty()
@staticmethod
def test_init_array():
"""Tests whether the __init__ function of :see:LocalizedValue properly