From 5133f81104712611ef5c89722ebab1391f6b529f Mon Sep 17 00:00:00 2001 From: Richard Terry Date: Thu, 15 Aug 2024 01:38:06 +0100 Subject: [PATCH] Fix form field ``has_changed`` refs #185 --- docs/changelog.rst | 13 +++++++ tagulous/forms.py | 13 +++++++ tagulous/models/fields.py | 81 ++++++++++++++++++++------------------- tagulous/utils.py | 2 +- 4 files changed, 68 insertions(+), 41 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 2fec9db..d9b17fe 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -13,6 +13,19 @@ Changes for upcoming releases will be listed without a release date - these are available by installing the develop branch from github. +2.1.0, TBC +---------- + +Bugfix: + +* Form field ``has_changed`` now correctly detects if a TagField has changed (#185) + +Thanks to: + +* Christian Schneider (cnschn) for help fixing ``has_changed`` (#185) + + + 2.0.0, 2024-08-13 ----------------- diff --git a/tagulous/forms.py b/tagulous/forms.py index d4afbaf..a6947b6 100644 --- a/tagulous/forms.py +++ b/tagulous/forms.py @@ -12,6 +12,7 @@ from . import settings from .models import options +from .models.fields import FakeQuerySet from .models.models import BaseTagModel, TagModelQuerySet from .utils import parse_tags, render_tags @@ -324,6 +325,18 @@ def prepare_value(self, value): # Deal with it as normal return super(TagField, self).prepare_value(value) + def _coerce(self, value): + """ + Standardise the value - it could be a fake queryset from the database object, or + a tag string from the form field. Split and sort the tags. + """ + if isinstance(value, FakeQuerySet): + tag_string = value[0] + else: + tag_string = value + tags = parse_tags(tag_string) + return render_tags(sorted(tags)) + # ############################################################################## # ###### Inline tagged formset diff --git a/tagulous/models/fields.py b/tagulous/models/fields.py index 25c6b88..c015944 100644 --- a/tagulous/models/fields.py +++ b/tagulous/models/fields.py @@ -385,6 +385,47 @@ def formfield(self, form_class=None, **kwargs): # ############################################################################## +class FakeObject(object): + """ + FakeObject so m2d can check obj.pk + """ + + def __init__(self, value): + self.pk = value + + def __str__(self): + return self.pk + + +class FakeQuerySet(object): + """ + FakeQuerySet so m2d can call qs.values_list() + Only contains one FakeObject instance + """ + + def __init__(self, obj): + self.obj = obj + self._result_cache = None + + def __iter__(self): + """ + Iterable so m2d can use in list comprehension + """ + yield self.obj + + def __len__(self): + return 1 + + def __getitem__(self, key): + return self.obj + + def values_list(self, *fields, **kwargs): + """ + Ignores arguments and returns an empty list with the object.pk + """ + return [self.obj.pk] + + class TagField(BaseTagField, models.ManyToManyField): """ Build the tag model and register the TagManyToManyField @@ -464,46 +505,6 @@ def value_from_object(self, obj): where the pk attribute is the tag string - a bit of a hack, but avoids monkey-patching Django. """ - - class FakeObject(object): - """ - FakeObject so m2d can check obj.pk - """ - - def __init__(self, value): - self.pk = value - - def __str__(self): - return self.pk - - class FakeQuerySet(object): - """ - FakeQuerySet so m2d can call qs.values_list() - Only contains one FakeObject instance - """ - - def __init__(self, obj): - self.obj = obj - self._result_cache = None - - def __iter__(self): - """ - Iterable so m2d can use in list comprehension - """ - yield self.obj - - def __len__(self): - return 1 - - def __getitem__(self, key): - return self.obj - - def values_list(self, *fields, **kwargs): - """ - Ignores arguments and returns an empty list with the object.pk - """ - return [self.obj.pk] - return FakeQuerySet(FakeObject(getattr(obj, self.attname).get_tag_string())) def formfield(self, form_class=None, **kwargs): diff --git a/tagulous/utils.py b/tagulous/utils.py index 3a71ea0..58b842a 100644 --- a/tagulous/utils.py +++ b/tagulous/utils.py @@ -13,7 +13,7 @@ # ############################################################################## -def parse_tags(tag_string, max_count=0, space_delimiter=True): +def parse_tags(tag_string: str, max_count=0, space_delimiter=True) -> list[str]: """ Tag parser