Skip to content

Commit 1ee1c82

Browse files
autocomplete widget validation and related object creation, added obj param to extra context methods, allow override back url in change view (#92)
* feat: autocomplete widget - validate restricted queryset and allow create new objects * fix: missing data during view instancing * triv: validate multiselect in widget * triv: pass obj to all methods of change view extra context * triv: added obj param to reflect admin changes * triv: fixed get_parent_instance_from_request for new objects * triv: style fix * triv: add option for autocomplete widget to reload parent change view when saving related model instance in modal * triv: removed unused import * triv: allow override back url in change form view * triv: raise ImproperlyConfigured for multiselect with allow_add * fix lxml version * triv: fixed case where newly created value didn't propagate to context data due to form being bound which is caused by other field error * triv: check for referer * triv: poetry lock * triv: update workflow * bump version * triv: revert form field changes * triv: save newly created value in autocomplete widget to request data and retrieve it in context data if there is other validation error in form * Revert "triv: pass obj to all methods of change view extra context" This reverts commit 41b61d8 * Revert "triv: added obj param to reflect admin changes" This reverts commit 34d23bf. * triv: black format
1 parent 0f6ba35 commit 1ee1c82

File tree

14 files changed

+716
-386
lines changed

14 files changed

+716
-386
lines changed

.github/workflows/workflow.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ jobs:
2424
- name: Set up Python
2525
uses: actions/setup-python@v4
2626
with:
27-
python-version: "3.x"
27+
python-version: "3.13"
28+
- name: Install system dependencies for lxml
29+
run: |
30+
sudo apt-get update
31+
sudo apt-get install -y libxml2-dev libxslt1-dev zlib1g-dev
2832
- name: Install dependencies
2933
run: |
3034
python3 -m pip install poetry==1.8.5

poetry.lock

Lines changed: 389 additions & 337 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "django-smartbase-admin"
3-
version = "1.0.33"
3+
version = "1.0.34"
44
description = ""
55
authors = ["SmartBase <info@smartbase.sk>"]
66
readme = "README.md"

src/django_smartbase_admin/admin/admin_base.py

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from collections.abc import Iterable
55
from functools import partial
66
from typing import Any
7+
from urllib.parse import urlparse
78

89
from ckeditor.fields import RichTextFormField
910
from ckeditor_uploader.fields import RichTextUploadingFormField
@@ -30,7 +31,7 @@
3031
from django.http import HttpResponse
3132
from django.template.loader import render_to_string
3233
from django.template.response import TemplateResponse
33-
from django.urls import reverse, NoReverseMatch
34+
from django.urls import reverse, NoReverseMatch, resolve
3435
from django.utils.safestring import mark_safe, SafeString
3536
from django.utils.text import capfirst
3637
from django.utils.translation import gettext_lazy as _
@@ -126,6 +127,7 @@
126127
SBADMIN_PARENT_INSTANCE_PK_VAR,
127128
SBADMIN_PARENT_INSTANCE_LABEL_VAR,
128129
SBADMIN_PARENT_INSTANCE_FIELD_NAME_VAR,
130+
SBADMIN_RELOAD_ON_SAVE_VAR,
129131
)
130132
from django_smartbase_admin.engine.const import (
131133
OBJECT_ID_PLACEHOLDER,
@@ -316,6 +318,13 @@ def __init__(self, *args, **kwargs):
316318
"request", SBAdminThreadLocalService.get_request()
317319
)
318320
super().__init__(*args, **kwargs)
321+
self.init_widgets_dynamic(threadsafe_request)
322+
for field in self.declared_fields:
323+
form_field = self.fields.get(field)
324+
if form_field:
325+
self.assign_widget_to_form_field(form_field, request=threadsafe_request)
326+
327+
def init_widgets_dynamic(self, request):
319328
for field in self.fields:
320329
if not hasattr(self.fields[field].widget, "init_widget_dynamic"):
321330
continue
@@ -324,12 +333,8 @@ def __init__(self, *args, **kwargs):
324333
self.fields[field],
325334
field,
326335
self.view,
327-
threadsafe_request,
336+
request,
328337
)
329-
for field in self.declared_fields:
330-
form_field = self.fields.get(field)
331-
if form_field:
332-
self.assign_widget_to_form_field(form_field, request=threadsafe_request)
333338

334339

335340
class SBAdminBaseForm(SBAdminBaseFormInit, forms.ModelForm):
@@ -822,7 +827,14 @@ def get_additional_filter_for_previous_next_context(self, request, object_id) ->
822827
return Q()
823828

824829
def get_change_view_context(self, request, object_id) -> dict | dict[str, Any]:
825-
return {"show_back_button": True}
830+
return {
831+
"show_back_button": True,
832+
"back_url": reverse(
833+
"sb_admin:{}_{}_changelist".format(
834+
self.opts.app_label, self.opts.model_name
835+
)
836+
),
837+
}
826838

827839
def get_previous_next_context(self, request, object_id) -> dict | dict[str, Any]:
828840
if not self.sbadmin_previous_next_buttons_enabled or not object_id:
@@ -958,7 +970,7 @@ def get_modal_save_response(cls, request, obj):
958970
"field": request.POST.get("sb_admin_source_field"),
959971
"id": obj.pk,
960972
"label": str(obj),
961-
"reload": request.POST.get("sbadmin_reload_on_save") == "1",
973+
"reload": request.POST.get(SBADMIN_RELOAD_ON_SAVE_VAR) == "1",
962974
},
963975
)
964976
trigger_client_event(response, "hideModal", {"elt": "sb-admin-modal"})
@@ -1051,6 +1063,31 @@ def register_autocomplete_views(self, request) -> None:
10511063
self.initialize_form_class(form_class, request)
10521064
form_class()
10531065

1066+
def get_parent_instance_from_request(self):
1067+
# Try to get parent instance from request referrer
1068+
request = (
1069+
getattr(self, "threadsafe_request", None)
1070+
or SBAdminThreadLocalService.get_request()
1071+
)
1072+
allowed = SBAdminViewService.has_permission(
1073+
request=request, model=self.parent_model, permission="view"
1074+
)
1075+
if not allowed:
1076+
return None
1077+
1078+
referer = request.META.get("HTTP_REFERER")
1079+
if not referer:
1080+
return None
1081+
resolved = resolve(urlparse(referer).path)
1082+
# Try common kwargs for object ID
1083+
object_id = resolved.kwargs.get("object_id")
1084+
if not object_id:
1085+
return None
1086+
base_qs = SBAdminViewService.get_restricted_queryset(
1087+
self.parent_model, request, request.request_data
1088+
)
1089+
return base_qs.get(pk=object_id)
1090+
10541091
def get_context_data(self, request) -> dict[str, Any]:
10551092
is_sortable_active: bool = self.sortable_field_name and (
10561093
self.has_add_permission(request) or self.has_change_permission(request)

0 commit comments

Comments
 (0)