Skip to content

Commit 0732186

Browse files
authored
Merge pull request #276 from American-Institutes-for-Research/HEA-1048/case_insensitive_indexes
Add case-insensitive indexes - see HEA-1048
2 parents c89bab1 + 7860c92 commit 0732186

2 files changed

Lines changed: 208 additions & 5 deletions

File tree

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# Generated by Django 5.2.12 on 2026-04-17 14:36
2+
3+
import django.db.models.functions.text
4+
from django.db import migrations, models
5+
6+
import common.fields
7+
8+
9+
class Migration(migrations.Migration):
10+
11+
dependencies = [
12+
("baseline", "0030_alter_foodpurchase_unit_multiple_and_more"),
13+
("common", "0011_additionalpermissions"),
14+
]
15+
16+
operations = [
17+
migrations.RemoveConstraint(
18+
model_name="community",
19+
name="baseline_community_livelihood_zone_baseline_full_name_uniq",
20+
),
21+
migrations.AlterField(
22+
model_name="livelihoodzone",
23+
name="name_en",
24+
field=common.fields.NameField(max_length=200, verbose_name="Name"),
25+
),
26+
migrations.AddConstraint(
27+
model_name="community",
28+
constraint=models.UniqueConstraint(
29+
models.F("livelihood_zone_baseline"),
30+
django.db.models.functions.text.Lower("full_name"),
31+
name="baseline_community_livelihood_zone_baseline_full_name_lower_uniq",
32+
),
33+
),
34+
migrations.AddConstraint(
35+
model_name="livelihoodzone",
36+
constraint=models.UniqueConstraint(
37+
django.db.models.functions.text.Lower("code"), name="baseline_livelihoodzone_code_lower_uniq"
38+
),
39+
),
40+
migrations.AddConstraint(
41+
model_name="livelihoodzone",
42+
constraint=models.UniqueConstraint(
43+
models.F("country"),
44+
django.db.models.functions.text.Lower("alternate_code"),
45+
condition=models.Q(("alternate_code__isnull", False), models.Q(("alternate_code", ""), _negated=True)),
46+
name="baseline_livelihoodzone_country_alternate_code_lower_uniq",
47+
),
48+
),
49+
migrations.AddConstraint(
50+
model_name="livelihoodzone",
51+
constraint=models.UniqueConstraint(
52+
django.db.models.functions.text.Lower("name_en"), name="baseline_livelihoodzone_name_en_lower_uniq"
53+
),
54+
),
55+
migrations.AddConstraint(
56+
model_name="livelihoodzone",
57+
constraint=models.UniqueConstraint(
58+
django.db.models.functions.text.Lower("name_ar"),
59+
condition=models.Q(("name_ar", ""), _negated=True),
60+
name="baseline_livelihoodzone_name_ar_lower_uniq",
61+
),
62+
),
63+
migrations.AddConstraint(
64+
model_name="livelihoodzone",
65+
constraint=models.UniqueConstraint(
66+
django.db.models.functions.text.Lower("name_es"),
67+
condition=models.Q(("name_es", ""), _negated=True),
68+
name="baseline_livelihoodzone_name_es_lower_uniq",
69+
),
70+
),
71+
migrations.AddConstraint(
72+
model_name="livelihoodzone",
73+
constraint=models.UniqueConstraint(
74+
django.db.models.functions.text.Lower("name_fr"),
75+
condition=models.Q(("name_fr", ""), _negated=True),
76+
name="baseline_livelihoodzone_name_fr_lower_uniq",
77+
),
78+
),
79+
migrations.AddConstraint(
80+
model_name="livelihoodzone",
81+
constraint=models.UniqueConstraint(
82+
django.db.models.functions.text.Lower("name_pt"),
83+
condition=models.Q(("name_pt", ""), _negated=True),
84+
name="baseline_livelihoodzone_name_pt_lower_uniq",
85+
),
86+
),
87+
migrations.RemoveConstraint(
88+
model_name="livelihoodzonebaseline",
89+
name="baseline_livelihoodzonebaseline_name_en_reference_year_end_date_uniq",
90+
),
91+
migrations.AddConstraint(
92+
model_name="livelihoodzonebaseline",
93+
constraint=models.UniqueConstraint(
94+
django.db.models.functions.text.Lower("name_en"),
95+
models.F("reference_year_end_date"),
96+
name="baseline_livelihoodzonebaseline_name_en_lower_uniq",
97+
),
98+
),
99+
migrations.AddConstraint(
100+
model_name="livelihoodzonebaseline",
101+
constraint=models.UniqueConstraint(
102+
django.db.models.functions.text.Lower("name_ar"),
103+
models.F("reference_year_end_date"),
104+
condition=models.Q(("name_ar", ""), _negated=True),
105+
name="baseline_livelihoodzonebaseline_name_ar_lower_uniq",
106+
),
107+
),
108+
migrations.AddConstraint(
109+
model_name="livelihoodzonebaseline",
110+
constraint=models.UniqueConstraint(
111+
django.db.models.functions.text.Lower("name_es"),
112+
models.F("reference_year_end_date"),
113+
condition=models.Q(("name_es", ""), _negated=True),
114+
name="baseline_livelihoodzonebaseline_name_es_lower_uniq",
115+
),
116+
),
117+
migrations.AddConstraint(
118+
model_name="livelihoodzonebaseline",
119+
constraint=models.UniqueConstraint(
120+
django.db.models.functions.text.Lower("name_fr"),
121+
models.F("reference_year_end_date"),
122+
condition=models.Q(("name_fr", ""), _negated=True),
123+
name="baseline_livelihoodzonebaseline_name_fr_lower_uniq",
124+
),
125+
),
126+
migrations.AddConstraint(
127+
model_name="livelihoodzonebaseline",
128+
constraint=models.UniqueConstraint(
129+
django.db.models.functions.text.Lower("name_pt"),
130+
models.F("reference_year_end_date"),
131+
condition=models.Q(("name_pt", ""), _negated=True),
132+
name="baseline_livelihoodzonebaseline_name_pt_lower_uniq",
133+
),
134+
),
135+
]

apps/baseline/models.py

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from django.core.exceptions import ValidationError
1212
from django.core.validators import MaxValueValidator, MinValueValidator
1313
from django.db.models import F, Q
14+
from django.db.models.functions import Lower
1415
from django.utils.translation import gettext_lazy as _
1516
from model_utils.managers import InheritanceManager
1617

@@ -120,14 +121,53 @@ class LivelihoodZone(common_models.Model):
120121
"Alternate identifier for the Livelihood Zone, typically a meaningful code based on the name of the Zone."
121122
), # NOQA: E501
122123
)
123-
name = TranslatedField(common_models.NameField(max_length=200, unique=True))
124+
name = TranslatedField(common_models.NameField(max_length=200))
124125
description = TranslatedField(common_models.DescriptionField())
125126
country = models.ForeignKey(Country, verbose_name=_("Country"), db_column="country_code", on_delete=models.PROTECT)
126127
objects = common_models.IdentifierManager.from_queryset(LivelihoodZoneQuerySet)()
127128

128129
class Meta:
129130
verbose_name = _("Livelihood Zone")
130131
verbose_name_plural = _("Livelihood Zones")
132+
constraints = [
133+
# Create a case-insensitive unique constraint on the code
134+
models.UniqueConstraint(
135+
Lower("code"),
136+
name="baseline_livelihoodzone_code_lower_uniq",
137+
),
138+
# Create a case-insensitive unique constraint on the country and the alternate code
139+
models.UniqueConstraint(
140+
"country",
141+
Lower("alternate_code"),
142+
condition=Q(alternate_code__isnull=False) & ~Q(alternate_code=""),
143+
name="baseline_livelihoodzone_country_alternate_code_lower_uniq",
144+
),
145+
# Create a case-insensitive unique constraint on the name
146+
models.UniqueConstraint(
147+
Lower("name_en"),
148+
name="baseline_livelihoodzone_name_en_lower_uniq",
149+
),
150+
models.UniqueConstraint(
151+
Lower("name_ar"),
152+
name="baseline_livelihoodzone_name_ar_lower_uniq",
153+
condition=~Q(name_ar=""),
154+
),
155+
models.UniqueConstraint(
156+
Lower("name_es"),
157+
name="baseline_livelihoodzone_name_es_lower_uniq",
158+
condition=~Q(name_es=""),
159+
),
160+
models.UniqueConstraint(
161+
Lower("name_fr"),
162+
name="baseline_livelihoodzone_name_fr_lower_uniq",
163+
condition=~Q(name_fr=""),
164+
),
165+
models.UniqueConstraint(
166+
Lower("name_pt"),
167+
name="baseline_livelihoodzone_name_pt_lower_uniq",
168+
condition=~Q(name_pt=""),
169+
),
170+
]
131171

132172
class ExtraMeta:
133173
identifier = ["code"]
@@ -300,9 +340,35 @@ class Meta:
300340
fields=("livelihood_zone", "reference_year_end_date"),
301341
name="baseline_livelihoodzonebaseline_livelihood_zone_reference_year_end_date_uniq",
302342
),
343+
# Create a case-insensitive unique constraint on the name
344+
models.UniqueConstraint(
345+
Lower("name_en"),
346+
"reference_year_end_date",
347+
name="baseline_livelihoodzonebaseline_name_en_lower_uniq",
348+
),
349+
models.UniqueConstraint(
350+
Lower("name_ar"),
351+
"reference_year_end_date",
352+
name="baseline_livelihoodzonebaseline_name_ar_lower_uniq",
353+
condition=~Q(name_ar=""),
354+
),
355+
models.UniqueConstraint(
356+
Lower("name_es"),
357+
"reference_year_end_date",
358+
name="baseline_livelihoodzonebaseline_name_es_lower_uniq",
359+
condition=~Q(name_es=""),
360+
),
361+
models.UniqueConstraint(
362+
Lower("name_fr"),
363+
"reference_year_end_date",
364+
name="baseline_livelihoodzonebaseline_name_fr_lower_uniq",
365+
condition=~Q(name_fr=""),
366+
),
303367
models.UniqueConstraint(
304-
fields=("name_en", "reference_year_end_date"),
305-
name="baseline_livelihoodzonebaseline_name_en_reference_year_end_date_uniq",
368+
Lower("name_pt"),
369+
"reference_year_end_date",
370+
name="baseline_livelihoodzonebaseline_name_pt_lower_uniq",
371+
condition=~Q(name_pt=""),
306372
),
307373
]
308374

@@ -458,9 +524,11 @@ class Meta:
458524
verbose_name = _("Community")
459525
verbose_name_plural = _("Communities")
460526
constraints = [
527+
# Create a case-insensitive unique constraint on the baseline and the full name.
461528
models.UniqueConstraint(
462-
fields=("livelihood_zone_baseline", "full_name"),
463-
name="baseline_community_livelihood_zone_baseline_full_name_uniq",
529+
"livelihood_zone_baseline",
530+
Lower("full_name"),
531+
name="baseline_community_livelihood_zone_baseline_full_name_lower_uniq",
464532
),
465533
# Create a unique constraint on id and livelihood_zone_baseline, so that we can use it as a target for a
466534
# composite foreign key from Seasonal Activity, which in turn allows us to ensure that the Community

0 commit comments

Comments
 (0)