Skip to content

Commit 604d6ed

Browse files
committed
Add a new Django field, IETFJSONField
This field is needed because the plain JSONField does not permit empty arrays - [] - or empty objects - {} - when the field is marked as required. Those values explicitly evaluate to a null value, and are rejected. Instead, the IETFJSONField accepts two new arguments to control this: - empty_values: An array of values that should evaluate to null/empty, and be rejected. - accepted_empty_values: An array of values that should *not* evaluate to null/empty, and be accepted. This allows the programmer to specify either a positive or negative statement of what values to accept. Fixes issue ietf-tools#3331. Commit ready for merge. - Legacy-Id: 19401
1 parent 58fa321 commit 604d6ed

File tree

3 files changed

+52
-8
lines changed

3 files changed

+52
-8
lines changed

ietf/group/models.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from ietf.group.colors import fg_group_colors, bg_group_colors
2424
from ietf.name.models import GroupStateName, GroupTypeName, DocTagName, GroupMilestoneStateName, RoleName, AgendaTypeName, ExtResourceName
2525
from ietf.person.models import Email, Person
26+
from ietf.utils.db import IETFJSONField
2627
from ietf.utils.mail import formataddr, send_mail_text
2728
from ietf.utils import log
2829
from ietf.utils.models import ForeignKey, OneToOneField
@@ -282,14 +283,14 @@ class GroupFeatures(models.Model):
282283
agenda_type = models.ForeignKey(AgendaTypeName, null=True, default="ietf", on_delete=CASCADE)
283284
about_page = models.CharField(max_length=64, blank=False, default="ietf.group.views.group_about" )
284285
default_tab = models.CharField(max_length=64, blank=False, default="ietf.group.views.group_about" )
285-
material_types = jsonfield.JSONField(max_length=64, blank=False, default=["slides"])
286-
default_used_roles = jsonfield.JSONField(max_length=256, blank=False, default=[])
287-
admin_roles = jsonfield.JSONField(max_length=64, blank=False, default=["chair"]) # Trac Admin
288-
docman_roles = jsonfield.JSONField(max_length=128, blank=False, default=["ad","chair","delegate","secr"])
289-
groupman_roles = jsonfield.JSONField(max_length=128, blank=False, default=["ad","chair",])
290-
groupman_authroles = jsonfield.JSONField(max_length=128, blank=False, default=["Secretariat",])
291-
matman_roles = jsonfield.JSONField(max_length=128, blank=False, default=["ad","chair","delegate","secr"])
292-
role_order = jsonfield.JSONField(max_length=128, blank=False, default=["chair","secr","member"],
286+
material_types = IETFJSONField(max_length=64, accepted_empty_values=[[], {}], blank=False, default=["slides"])
287+
default_used_roles = IETFJSONField(max_length=256, accepted_empty_values=[[], {}], blank=False, default=[])
288+
admin_roles = IETFJSONField(max_length=64, accepted_empty_values=[[], {}], blank=False, default=["chair"]) # Trac Admin
289+
docman_roles = IETFJSONField(max_length=128, accepted_empty_values=[[], {}], blank=False, default=["ad","chair","delegate","secr"])
290+
groupman_roles = IETFJSONField(max_length=128, accepted_empty_values=[[], {}], blank=False, default=["ad","chair",])
291+
groupman_authroles = IETFJSONField(max_length=128, accepted_empty_values=[[], {}], blank=False, default=["Secretariat",])
292+
matman_roles = IETFJSONField(max_length=128, accepted_empty_values=[[], {}], blank=False, default=["ad","chair","delegate","secr"])
293+
role_order = IETFJSONField(max_length=128, accepted_empty_values=[[], {}], blank=False, default=["chair","secr","member"],
293294
help_text="The order in which roles are shown, for instance on photo pages. Enter valid JSON.")
294295

295296

ietf/utils/db.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Copyright The IETF Trust 2021, All Rights Reserved
2+
# -*- coding: utf-8 -*-
3+
4+
# Taken from/inspired by
5+
# https://stackoverflow.com/questions/55147169/django-admin-jsonfield-default-empty-dict-wont-save-in-admin
6+
#
7+
# JSONField should recognize {}, (), and [] as valid, non-empty JSON
8+
# values. However, the base Field class excludes them
9+
import jsonfield
10+
11+
from ietf.utils.fields import IETFJSONField as FormIETFJSONField
12+
13+
14+
class IETFJSONField(jsonfield.JSONField):
15+
form_class = FormIETFJSONField
16+
17+
def __init__(self, *args, empty_values=FormIETFJSONField.empty_values, accepted_empty_values=None, **kwargs):
18+
if accepted_empty_values is None:
19+
accepted_empty_values = []
20+
self.empty_values = [x
21+
for x in empty_values
22+
if x not in accepted_empty_values]
23+
super().__init__(*args, **kwargs)
24+
25+
def formfield(self, **kwargs):
26+
if issubclass(kwargs['form_class'], FormIETFJSONField):
27+
kwargs.setdefault('empty_values', self.empty_values)
28+
return super().formfield(**{**kwargs})

ietf/utils/fields.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import json
77
import re
88

9+
import jsonfield
10+
911
import debug # pyflakes:ignore
1012

1113
from typing import Optional, Type # pyflakes:ignore
@@ -265,6 +267,19 @@ def clean(self, value):
265267

266268
return objs.first() if self.max_entries == 1 else objs
267269

270+
271+
class IETFJSONField(jsonfield.fields.forms.JSONField):
272+
def __init__(self, *args, empty_values=jsonfield.fields.forms.JSONField.empty_values,
273+
accepted_empty_values=None, **kwargs):
274+
if accepted_empty_values is None:
275+
accepted_empty_values = []
276+
self.empty_values = [x
277+
for x in empty_values
278+
if x not in accepted_empty_values]
279+
280+
super().__init__(*args, **kwargs)
281+
282+
268283
class MissingOkImageField(models.ImageField):
269284
"""Image field that can validate successfully if file goes missing
270285

0 commit comments

Comments
 (0)