Adds validation for ObjectListWidget.ConfigForm.model field

This commit is contained in:
Jason Novinger 2025-03-10 09:57:45 -05:00
parent 528248b560
commit 76c3c613a9
2 changed files with 38 additions and 0 deletions

View File

@ -9,6 +9,7 @@ import requests
from django import forms from django import forms
from django.conf import settings from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
from django.db.models import Model
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.urls import NoReverseMatch, resolve, reverse from django.urls import NoReverseMatch, resolve, reverse
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
@ -42,6 +43,27 @@ def get_object_type_choices():
] ]
def object_list_widget_supports_model(model: Model) -> bool:
"""Test whether a model is supported by the ObjectListWidget
In theory there could be more than one reason why a model isn't supported by the
ObjectListWidget, although we've only identified one so far--there's no resolve-able 'list' URL
for the model. Add more tests if more conditions arise.
"""
def can_resolve_model_list_view(model: Model) -> bool:
try:
reverse(get_viewname(model, action='list'))
return True
except Exception:
return False
tests = [
can_resolve_model_list_view,
]
return all(test(model) for test in tests)
def get_bookmarks_object_type_choices(): def get_bookmarks_object_type_choices():
return [ return [
(object_type_identifier(ot), object_type_name(ot)) (object_type_identifier(ot), object_type_name(ot))
@ -234,6 +256,17 @@ class ObjectListWidget(DashboardWidget):
raise forms.ValidationError(_("Invalid format. URL parameters must be passed as a dictionary.")) raise forms.ValidationError(_("Invalid format. URL parameters must be passed as a dictionary."))
return data return data
def clean_model(self):
if model_info := self.cleaned_data['model']:
app_label, model_name = model_info.split('.')
model = ObjectType.objects.get_by_natural_key(app_label, model_name).model_class()
if not object_list_widget_supports_model(model):
raise forms.ValidationError(
_(f"Invalid model selection: {self['model'].data} is not supported.")
)
return model_info
def render(self, request): def render(self, request):
app_label, model_name = self.config['model'].split('.') app_label, model_name = self.config['model'].split('.')
model = ObjectType.objects.get_by_natural_key(app_label, model_name).model_class() model = ObjectType.objects.get_by_natural_key(app_label, model_name).model_class()

View File

@ -4,6 +4,11 @@ from extras.dashboard.widgets import ObjectListWidget
class ObjectListWidgetTests(TestCase): class ObjectListWidgetTests(TestCase):
def test_widget_config_form_validates_model(self):
model_info = 'extras.notification'
form = ObjectListWidget.ConfigForm({'model': model_info})
self.assertFalse(form.is_valid())
@tag('regression') @tag('regression')
def test_widget_fails_gracefully(self): def test_widget_fails_gracefully(self):
""" """