From 84107ee11ab9ab05f58902f71735a103d1845857 Mon Sep 17 00:00:00 2001 From: Eric Vyncke Date: Sat, 18 Jul 2026 09:33:03 +0000 Subject: [PATCH 1/2] Use Django Collate() when listing all ADs --- ietf/doc/forms.py | 3 ++- ietf/doc/views_charter.py | 3 ++- ietf/doc/views_conflict_review.py | 3 ++- ietf/doc/views_draft.py | 4 +++- ietf/doc/views_status_change.py | 3 ++- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/ietf/doc/forms.py b/ietf/doc/forms.py index 768d6f96af2..bbfb821c3a5 100644 --- a/ietf/doc/forms.py +++ b/ietf/doc/forms.py @@ -7,6 +7,7 @@ from django import forms from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.core.validators import validate_email +from django.db.models.functions import Collate from ietf.doc.fields import SearchableDocumentField, SearchableDocumentsField from ietf.doc.models import RelatedDocument, DocExtResource, State @@ -63,7 +64,7 @@ class DocAuthorChangeBasisForm(forms.Form): help_text='What is the source or reasoning for the changes to the author list?') class AdForm(forms.Form): - ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active", role__group__type='area').order_by('name'), + ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active", role__group__type='area').order_by(Collate('name', "en-US-x-icu")), label="Shepherding AD", empty_label="(None)", required=True) def __init__(self, *args, **kwargs): diff --git a/ietf/doc/views_charter.py b/ietf/doc/views_charter.py index e899f592271..983bf5b66e5 100644 --- a/ietf/doc/views_charter.py +++ b/ietf/doc/views_charter.py @@ -20,6 +20,7 @@ from django.utils import timezone from django.utils.encoding import force_str from django.utils.html import escape +from django.db.models.functions import Collate import debug # pyflakes:ignore @@ -305,7 +306,7 @@ def change_title(request, name, option=None): )) class AdForm(forms.Form): - ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active", role__group__type="area").order_by('name'), + ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active", role__group__type="area").order_by(Collate('name', "en-US-x-icu")), label="Responsible AD", empty_label="(None)", required=True) def __init__(self, *args, **kwargs): diff --git a/ietf/doc/views_conflict_review.py b/ietf/doc/views_conflict_review.py index 159f1340a49..56ea3e10463 100644 --- a/ietf/doc/views_conflict_review.py +++ b/ietf/doc/views_conflict_review.py @@ -14,6 +14,7 @@ from django.template.loader import render_to_string from django.conf import settings from django.utils.html import escape +from django.db.models.functions import Collate import debug # pyflakes:ignore @@ -404,7 +405,7 @@ class SimpleStartReviewForm(forms.Form): ) class StartReviewForm(forms.Form): - ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active",role__group__type='area').order_by('name'), + ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active",role__group__type='area').order_by(Collate('name', "en-US-x-icu")), label="Shepherding AD", empty_label="(None)", required=True) create_in_state = forms.ModelChoiceField(State.objects.filter(used=True, type="conflrev", slug__in=("needshep", "adrev")), empty_label=None, required=False) notify = forms.CharField( diff --git a/ietf/doc/views_draft.py b/ietf/doc/views_draft.py index a64d0a53fe9..2b4f87706ac 100644 --- a/ietf/doc/views_draft.py +++ b/ietf/doc/views_draft.py @@ -20,6 +20,8 @@ from django.forms.utils import ErrorList from django.template.defaultfilters import pluralize from django.utils import timezone +from django.db.models.functions import Collate + import debug # pyflakes:ignore @@ -1130,7 +1132,7 @@ class AdForm(forms.Form): role__name__in=("ad", "pre-ad"), role__group__state="active", role__group__type="area", - ).order_by('name'), + ).order_by(Collate('name', "en-US-x-icu")), label="Shepherding AD", empty_label="(None)", required=False, diff --git a/ietf/doc/views_status_change.py b/ietf/doc/views_status_change.py index 2bccc213c40..96f41fab5dd 100644 --- a/ietf/doc/views_status_change.py +++ b/ietf/doc/views_status_change.py @@ -18,6 +18,7 @@ from django.conf import settings from django.utils.encoding import force_str from django.utils.html import escape +from django.db.models.functions import Collate import debug # pyflakes:ignore from ietf.doc.mails import email_ad_approved_status_change @@ -484,7 +485,7 @@ def clean(self): class StartStatusChangeForm(forms.Form): document_name = forms.CharField(max_length=255, label="Document name", help_text="A descriptive name such as status-change-md2-to-historic is better than status-change-rfc1319.", required=True) title = forms.CharField(max_length=255, label="Title", required=True) - ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active",role__group__type='area').order_by('name'), + ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active",role__group__type='area').order_by(Collate('name', "en-US-x-icu")), label="Shepherding AD", empty_label="(None)", required=False) create_in_state = forms.ModelChoiceField(State.objects.filter(type="statchg", slug__in=("needshep", "adrev")), empty_label=None, required=False) notify = forms.CharField( From 3491ebf6688eba6634ba7aa7dc86626a9611181b Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Wed, 12 Aug 2026 23:06:57 -0300 Subject: [PATCH 2/2] refactor: move collation value to settings --- ietf/doc/forms.py | 11 +++++++++-- ietf/doc/views_charter.py | 10 ++++++++-- ietf/doc/views_conflict_review.py | 10 ++++++++-- ietf/doc/views_draft.py | 2 +- ietf/doc/views_status_change.py | 10 ++++++++-- ietf/settings.py | 8 ++++++++ 6 files changed, 42 insertions(+), 9 deletions(-) diff --git a/ietf/doc/forms.py b/ietf/doc/forms.py index bbfb821c3a5..82076d31733 100644 --- a/ietf/doc/forms.py +++ b/ietf/doc/forms.py @@ -5,6 +5,7 @@ import datetime import debug #pyflakes:ignore from django import forms +from django.conf import settings from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.core.validators import validate_email from django.db.models.functions import Collate @@ -64,8 +65,14 @@ class DocAuthorChangeBasisForm(forms.Form): help_text='What is the source or reasoning for the changes to the author list?') class AdForm(forms.Form): - ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active", role__group__type='area').order_by(Collate('name', "en-US-x-icu")), - label="Shepherding AD", empty_label="(None)", required=True) + ad = forms.ModelChoiceField( + Person.objects.filter( + role__name="ad", role__group__state="active", role__group__type="area" + ).order_by(Collate("name", settings.PREFERRED_COLLATION)), + label="Shepherding AD", + empty_label="(None)", + required=True, + ) def __init__(self, *args, **kwargs): super(self.__class__, self).__init__(*args, **kwargs) diff --git a/ietf/doc/views_charter.py b/ietf/doc/views_charter.py index 983bf5b66e5..b2f400d6d6f 100644 --- a/ietf/doc/views_charter.py +++ b/ietf/doc/views_charter.py @@ -306,8 +306,14 @@ def change_title(request, name, option=None): )) class AdForm(forms.Form): - ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active", role__group__type="area").order_by(Collate('name', "en-US-x-icu")), - label="Responsible AD", empty_label="(None)", required=True) + ad = forms.ModelChoiceField( + Person.objects.filter( + role__name="ad", role__group__state="active", role__group__type="area" + ).order_by(Collate("name", settings.PREFERRED_COLLATION)), + label="Responsible AD", + empty_label="(None)", + required=True, + ) def __init__(self, *args, **kwargs): super(self.__class__, self).__init__(*args, **kwargs) diff --git a/ietf/doc/views_conflict_review.py b/ietf/doc/views_conflict_review.py index 56ea3e10463..495958133ad 100644 --- a/ietf/doc/views_conflict_review.py +++ b/ietf/doc/views_conflict_review.py @@ -405,8 +405,14 @@ class SimpleStartReviewForm(forms.Form): ) class StartReviewForm(forms.Form): - ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active",role__group__type='area').order_by(Collate('name', "en-US-x-icu")), - label="Shepherding AD", empty_label="(None)", required=True) + ad = forms.ModelChoiceField( + Person.objects.filter( + role__name="ad", role__group__state="active", role__group__type="area" + ).order_by(Collate("name", settings.PREFERRED_COLLATION)), + label="Shepherding AD", + empty_label="(None)", + required=True, + ) create_in_state = forms.ModelChoiceField(State.objects.filter(used=True, type="conflrev", slug__in=("needshep", "adrev")), empty_label=None, required=False) notify = forms.CharField( widget=forms.Textarea, diff --git a/ietf/doc/views_draft.py b/ietf/doc/views_draft.py index 2b4f87706ac..a0904afa0bc 100644 --- a/ietf/doc/views_draft.py +++ b/ietf/doc/views_draft.py @@ -1132,7 +1132,7 @@ class AdForm(forms.Form): role__name__in=("ad", "pre-ad"), role__group__state="active", role__group__type="area", - ).order_by(Collate('name', "en-US-x-icu")), + ).order_by(Collate('name', settings.PREFERRED_COLLATION)), label="Shepherding AD", empty_label="(None)", required=False, diff --git a/ietf/doc/views_status_change.py b/ietf/doc/views_status_change.py index 96f41fab5dd..a5db6e4bdd0 100644 --- a/ietf/doc/views_status_change.py +++ b/ietf/doc/views_status_change.py @@ -485,8 +485,14 @@ def clean(self): class StartStatusChangeForm(forms.Form): document_name = forms.CharField(max_length=255, label="Document name", help_text="A descriptive name such as status-change-md2-to-historic is better than status-change-rfc1319.", required=True) title = forms.CharField(max_length=255, label="Title", required=True) - ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active",role__group__type='area').order_by(Collate('name', "en-US-x-icu")), - label="Shepherding AD", empty_label="(None)", required=False) + ad = forms.ModelChoiceField( + Person.objects.filter( + role__name="ad", role__group__state="active", role__group__type="area" + ).order_by(Collate("name", settings.PREFERRED_COLLATION)), + label="Shepherding AD", + empty_label="(None)", + required=False, + ) create_in_state = forms.ModelChoiceField(State.objects.filter(type="statchg", slug__in=("needshep", "adrev")), empty_label=None, required=False) notify = forms.CharField( widget=forms.Textarea, diff --git a/ietf/settings.py b/ietf/settings.py index d2a622d63e6..483b08bff47 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -121,6 +121,14 @@ } +# Collation that we wish we were using. The production database is currently using +# the C collation, which does not handle accented characters. Do not change this +# without confirming that the production and dev databases support the new collation. +# This setting and places we use it can go away if we switch the production database +# collation. That requires creating and populating a new database, it cannot be done +# on an existing one. +PREFERRED_COLLATION = "en-US-x-icu" + # Local time zone for this installation. Choices can be found here: # http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE # although not all variations may be possible on all operating systems.