From 01301c9f97f8edd4108aa86e92f1a1806dcf7025 Mon Sep 17 00:00:00 2001 From: Andy Cook Date: Wed, 1 Apr 2026 16:02:04 +0100 Subject: [PATCH 1/3] Fix Django 5.0 compatibility - Replace removed smart_unicode/force_text with smart_str/force_str - Replace removed ugettext_lazy with gettext_lazy - Fix ChoiceField._set_choices removed in Django 5.0; use forms.ChoiceField.choices.fset() instead Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- mongotools/forms/fields.py | 20 +++++++++++--------- mongotools/forms/utils.py | 12 ++++++------ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/mongotools/forms/fields.py b/mongotools/forms/fields.py index bd17397..a4b123d 100644 --- a/mongotools/forms/fields.py +++ b/mongotools/forms/fields.py @@ -1,10 +1,9 @@ from django import forms -from django.utils.encoding import smart_unicode +from django.utils.encoding import smart_str, force_str from pymongo.errors import InvalidId from bson import ObjectId from django.core.validators import EMPTY_VALUES -from django.utils.encoding import smart_unicode, force_text -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from mongoengine import ReferenceField as MongoReferenceField @@ -36,7 +35,7 @@ class MongoCharField(forms.CharField): def to_python(self, value): if value in EMPTY_VALUES: return None - return smart_unicode(value) + return smart_str(value) class ReferenceField(forms.TypedChoiceField): """ @@ -74,7 +73,10 @@ def _set_queryset(self, queryset): def _get_choices(self): return MongoChoiceIterator(self) - choices = property(_get_choices, forms.ChoiceField._set_choices) + def _set_choices(self, value): + forms.ChoiceField.choices.fset(self, value) + + choices = property(_get_choices, _set_choices) def label_from_instance(self, obj): """ @@ -82,7 +84,7 @@ def label_from_instance(self, obj): generate the labels for the choices presented by this object. Subclasses can override this method to customize the display of the choices. """ - return smart_unicode(obj) + return smart_str(obj) def clean(self, oid): if oid in EMPTY_VALUES and not self.required: @@ -132,9 +134,9 @@ def clean(self, value): raise forms.ValidationError(self.error_messages['invalid_pk_value'] % pk) qs = self.queryset.clone() qs = qs.filter(**{'%s__in' % key: filter_ids}) - pks = set([force_text(getattr(o, key)) for o in qs]) + pks = set([force_str(getattr(o, key)) for o in qs]) for val in value: - if force_text(val) not in pks: + if force_str(val) not in pks: raise forms.ValidationError(self.error_messages['invalid_choice'] % val) # Since this overrides the inherited ModelChoiceField.clean # we run custom validators here @@ -182,7 +184,7 @@ def get_field_choices(self, field, include_blank=True, def string_field(self, value): if value in EMPTY_VALUES: return None - return smart_unicode(value) + return smart_str(value) def integer_field(self, value): if value in EMPTY_VALUES: diff --git a/mongotools/forms/utils.py b/mongotools/forms/utils.py index 6db237a..8078e29 100644 --- a/mongotools/forms/utils.py +++ b/mongotools/forms/utils.py @@ -3,11 +3,11 @@ import gridfs from django import forms -from mongoengine.base import ValidationError +from mongoengine.errors import ValidationError from mongoengine.fields import EmbeddedDocumentField, ListField, ReferenceField -from mongoengine.connection import _get_db +from mongoengine.connection import get_db -from fields import MongoFormFieldGenerator +from mongotools.forms.fields import MongoFormFieldGenerator import logging logger = logging.getLogger(__name__) @@ -32,7 +32,7 @@ def inner_validate(value, *args, **kwargs): try: new_clean(value) return value - except ValidationError, e: + except ValidationError as e: raise forms.ValidationError(e) return inner_validate @@ -68,12 +68,12 @@ def iter_valid_fields(meta): yield (field_name, field) def _get_unique_filename(name): - fs = gridfs.GridFS(_get_db()) + fs = gridfs.GridFS(get_db()) file_root, file_ext = os.path.splitext(name) count = itertools.count(1) while fs.exists(filename=name): # file_ext includes the dot. - name = os.path.join("%s_%s%s" % (file_root, count.next(), file_ext)) + name = os.path.join("%s_%s%s" % (file_root, next(count), file_ext)) return name def save_file(instance, field_name, file): From 21c8df1a41473b17a02f056e1deef516bd64fc36 Mon Sep 17 00:00:00 2001 From: Andy Cook Date: Tue, 14 Apr 2026 13:17:29 +0100 Subject: [PATCH 2/3] Py3 fix for metaclass --- mongotools/forms/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mongotools/forms/__init__.py b/mongotools/forms/__init__.py index e674c88..0538ee4 100644 --- a/mongotools/forms/__init__.py +++ b/mongotools/forms/__init__.py @@ -80,10 +80,9 @@ def __new__(cls, name, bases, attrs): return new_class -class MongoForm(forms.BaseForm): +class MongoForm(forms.BaseForm, metaclass=MongoFormMetaClass): """Base MongoForm class. Used to create new MongoForms""" - __metaclass__ = MongoFormMetaClass def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, initial=None, error_class=forms.utils.ErrorList, label_suffix=':', From d58517c0f7e638c34f841ee979c9da593289b5fe Mon Sep 17 00:00:00 2001 From: Andy Cook Date: Fri, 17 Apr 2026 16:27:14 +0100 Subject: [PATCH 3/3] Fix Python 3 / Django 5.x compatibility in MongoFormMetaClass - Use list(attrs.items()) to avoid RuntimeError from mutating dict during iteration (attrs.pop() inside the comprehension) - Remove fields.sort(lambda) call: cmp() doesn't exist in Python 3, and creation_counter was removed in Django 5.x. Python 3.7+ dicts preserve insertion order, making the sort unnecessary. - Use list(base.base_fields.items()) for Python 3 compatibility (dict.items() returns a view, not a list, so + concatenation fails) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- mongotools/forms/__init__.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mongotools/forms/__init__.py b/mongotools/forms/__init__.py index 0538ee4..cb99860 100644 --- a/mongotools/forms/__init__.py +++ b/mongotools/forms/__init__.py @@ -19,14 +19,12 @@ class MongoFormMetaClass(type): def __new__(cls, name, bases, attrs): # get all valid existing Fields and sort them fields = [(field_name, attrs.pop(field_name)) for field_name, obj in - attrs.items() if isinstance(obj, forms.Field)] - fields.sort( - lambda x, y: cmp(x[1].creation_counter, y[1].creation_counter)) + list(attrs.items()) if isinstance(obj, forms.Field)] # get all Fields from base classes for base in bases[::-1]: if hasattr(base, 'base_fields'): - fields = base.base_fields.items() + fields + fields = list(base.base_fields.items()) + fields # add the fields as "our" base fields attrs['base_fields'] = OrderedDict(fields)