[UPDT] HORILLA VIEWS: Generic solution to add addtional data along with dynamic create fields and fix group by action column width to default width

This commit is contained in:
Horilla
2024-12-16 16:17:15 +05:30
parent 31c8cba19c
commit 528a4d8375
11 changed files with 135 additions and 32 deletions

View File

@@ -1,5 +1,5 @@
from django.contrib import admin
from horilla_views.models import ActiveGroup, ActiveTab, ToggleColumn
from horilla_views.models import ActiveGroup, ActiveTab, SavedFilter, ToggleColumn
admin.site.register([ToggleColumn, ActiveTab, ActiveGroup])

View File

@@ -30,7 +30,6 @@ from django.utils.html import format_html
from django.utils.safestring import SafeString
from django.utils.translation import gettext_lazy as _trans
from base.methods import eval_validate
from horilla import settings
from horilla.horilla_middlewares import _thread_locals
from horilla_views.templatetags.generic_template_filters import getattribute
@@ -473,6 +472,8 @@ def value_to_field(field: object, value: list) -> Any:
"""
return value according to the format of the field
"""
from base.methods import eval_validate
if isinstance(field, models.ManyToManyField):
return [int(val) for val in value]
elif isinstance(

View File

@@ -294,13 +294,14 @@ class HorillaListView(ListView):
keys_to_remove = [
key
for key, value in data_dict.items()
if value[0] in ["unknown", "on"] + self.filter_keys_to_remove
if key in ["filter_applied", "nav_url"] + self.filter_keys_to_remove
]
for key in keys_to_remove + ["referrer"]:
if key in data_dict.keys():
data_dict.pop(key)
context["filter_dict"] = data_dict
context["keys_to_remove"] = keys_to_remove
request = self.request
ordered_ids = list(queryset.values_list("id", flat=True))
@@ -813,6 +814,8 @@ class HorillaFormView(FormView):
# NOTE: Dynamic create view's forms save method will be overwritten
is_dynamic_create_view: bool = False
# [(field_name,DynamicFormView,[other_field1,...])] # other_fields
# can be mentioned like this to pass the field selected
dynamic_create_fields: list = []
def __init__(self, **kwargs: Any) -> None:
@@ -903,7 +906,10 @@ class HorillaFormView(FormView):
files = self.request.FILES
form = self.init_form(data=data, files=files, instance=instance)
if self.is_dynamic_create_view:
setattr(type(form), "save", save)
# setattr(type(form), "save", save)
from types import MethodType
form.save = MethodType(save, form)
if self.request.method == "GET":
[
@@ -916,6 +922,9 @@ class HorillaFormView(FormView):
view = dynamic_tuple[1]
view.display_title = "Dynamic create"
field = dynamic_tuple[0]
additional_data_fields = []
if len(dynamic_tuple) == 3:
additional_data_fields = dynamic_tuple[2]
key = self.request.session.session_key + "cbv" + field
field_instance = form.instance._meta.get_field(field)
value = form.initial.get(field, [])
@@ -931,7 +940,6 @@ class HorillaFormView(FormView):
)
else:
value = getattr(getattribute(form.instance, field), "pk", value)
CACHE.set(
key,
{
@@ -957,6 +965,22 @@ class HorillaFormView(FormView):
choices.insert(0, ("", "Select option"))
choices.append(("dynamic_create", "Dynamic create"))
attrs = form.fields[field].widget.attrs
for data_field in additional_data_fields:
data_field_attr = form.fields[data_field].widget.attrs
if (
f"$('#modalButton{field}Form [name={data_field}]').val(this.value);"
not in data_field_attr.get("onchange", "")
):
data_field_attr["onchange"] = (
data_field_attr.get("onchange", "")
+ f"""
if(this.value != 'dynamic_create'){{
$('#modalButton{field}Form [name={data_field}]').val(this.value);
}}
"""
)
form.fields[field] = form_field(
choices=choices,
label=form.fields[field].label,
@@ -967,6 +991,18 @@ class HorillaFormView(FormView):
)
form.fields[field].widget.attrs = attrs
form.initial[field] = value
for dynamic_tuple in self.dynamic_create_fields:
field = dynamic_tuple[0]
onchange = form.fields[field].widget.attrs.get("onchange", "")
if onchange:
CACHE.set(
self.request.session.session_key
+ "cbv"
+ field
+ "onchange",
onchange,
)
if pk:
form.instance = instance
title = str(instance)
@@ -1004,6 +1040,7 @@ class HorillaNavView(TemplateView):
filter_instance: FilterSet = None
filter_instance_context_name: str = ""
filter_body_template: str = ""
empty_inputs: list = []
view_types: list = []
create_attrs: str = """"""
@@ -1027,8 +1064,12 @@ class HorillaNavView(TemplateView):
context["search_in"] = self.search_in
context["filter_instance_context_name"] = self.filter_instance
last_filter = CACHE.get(
self.request.session.session_key + "last-applied-filter", {}
self.request.session.session_key
+ "last-applied-filter"
+ self.request.path,
{},
)
context["empty_inputs"] = self.empty_inputs + ["nav_url"]
context["last_filter"] = dict(last_filter)
if self.filter_instance:
context[self.filter_form_context_name] = self.filter_instance.form

View File

@@ -14,6 +14,9 @@
<div style="display: none">{{filter_dict}}</div>
<script>
function fieldLabel(value, field) {
if ({{keys_to_remove|safe}}.includes(field)) {
return ""
}
fiedlElem = $(`#applyFilter`).closest(`form`).find(`[name=${field}]`);
if (fiedlElem.is("select")) {
// my conditions

View File

@@ -216,10 +216,12 @@
</table>
<script>
$(document).ready(function () {
attrAction =`{{header_attrs.action|safe}}`
if (!attrAction){
let thWidth = 0;
const widths = [];
$('.lastTd').each(function () {
$('#{{view_id}} .lastTd').each(function () {
widths.push(this.scrollWidth);
$(this).css('overflow', 'hidden');
});
@@ -229,8 +231,12 @@
if (widths.length) {
thWidth = widths[0];
}
if (!thWidth) {
thWidth = 180
}
$('.lastTh').css("width", `${thWidth}px`);
$('#{{view_id}} .lastTh').css("width", `${thWidth}px`);
}
});
</script>
</div>

View File

@@ -1,3 +1,4 @@
{% load generic_template_filters %}
<div id="{{view_id}}">
{% for field_tuple in dynamic_create_fields %}
<div
@@ -12,18 +13,28 @@
id="dynamicModal{{field_tuple.0}}Body"
></div>
</div>
</div>
{% endfor %}
<form id="{{view_id}}Form" hx-post="{{request.path}}?{{request.GET.urlencode}}" hx-encoding="multipart/form-data" hx-swap="outerHTML" {% if hx_confirm %} hx-confirm="{{hx_confirm}}" {% endif %}>{{form.structured}}</form>
{% for field_tuple in dynamic_create_fields %}
<div >
<script class="dynamic_{{field_tuple.0}}_scripts">
{{form.initial|get_item:field_tuple.0}}
$("#{{view_id}}Form [name={{field_tuple.0}}]").val({{form.initial|get_item:field_tuple.0|safe}}).change()
</script>
<form
hidden
id="modalButton{{field_tuple.0}}Form"
hx-get="/dynamic-path-{{field_tuple.0}}-{{request.session.session_key}}?dynamic_field={{field_tuple.0}}"
hx-target="#dynamicModal{{field_tuple.0}}Body"
>
<input type="hidden" name="dynamic_initial" data-dynamic-field="{{field_tuple.0}}">
<input type="hidden" name="view_id" value="{{view_id}}">
<input type="text" name="dynamic_initial" data-dynamic-field="{{field_tuple.0}}">
<input type="text" name="view_id" value="{{view_id}}">
{% for field in field_tuple.2 %}
<input type="text" name="{{field}}">
{% endfor %}
<button
hidden
type="submit"
id="modalButton{{field_tuple.0}}"
onclick="$('#dynamicModal{{field_tuple.0}}').addClass('oh-modal--show');"
@@ -31,16 +42,19 @@
{{field_tuple.0}}
</button>
</form>
<form id="reload-field{{field_tuple.0}}{{view_id}}" hx-get="{% url "reload-field" %}?form_class_path={{form_class_path}}&dynamic_field={{field_tuple.0}}" hx-target="#dynamic_field_{{field_tuple.0}}">
<input type="hidden" name="dynamic_initial" data-dynamic-field="{{field_tuple.0}}">
<input type="hidden" name="view_id" value="{{view_id}}">
<button hidden class="reload-field" data-target="{{field_tuple.0}}">
Reload Field
<form hidden id="reload-field{{field_tuple.0}}{{view_id}}" hx-get="{% url "reload-field" %}?form_class_path={{form_class_path}}&dynamic_field={{field_tuple.0}}" hx-target="#dynamic_field_{{field_tuple.0}}">
<input type="text" name="dynamic_initial" data-dynamic-field="{{field_tuple.0}}">
<input type="text" name="view_id" value="{{view_id}}">
<button class="reload-field" data-target="{{field_tuple.0}}">
{{field_tuple.0}}
</button>
</form>
<script>
<script class="dynamic_{{field_tuple.0}}_scripts">
$("#{{view_id}}Form [name={{field_tuple.0}}]").change(function (e) {
values = $(this).val();
if (!values) {
values = ""
}
if (values == "dynamic_create") {
$("#modalButton{{field_tuple.0}}").click()
$(this).val("")
@@ -51,6 +65,9 @@
$("#modalButton{{field_tuple.0}}").parent().find('input[name=dynamic_initial]').val(values)
$("#reload-field{{field_tuple.0}}{{view_id}}").find('input[name=dynamic_initial]').val(values)
$("#modalButton{{field_tuple.0}}").click()
}else if(values) {
$("#modalButton{{field_tuple.0}}").parent().find('input[name=dynamic_initial]').val(values)
$("#reload-field{{field_tuple.0}}{{view_id}}").find('input[name=dynamic_initial]').val(values)
}
});
$("#reload-field{{field_tuple.0}}{{view_id}}").submit(function (e) {
@@ -58,5 +75,6 @@
$(this).find("[name=dynamic_initial]").val();
});
</script>
</div>
{% endfor %}
</div>

View File

@@ -335,6 +335,9 @@
if (widths.length) {
thWidth = widths[0];
}
if (!thWidth) {
thWidth = 180
}
$('#{{view_id}} .lastTh').css("width", `${thWidth}px`);
}
});

View File

@@ -24,6 +24,7 @@
hx-on:submit="htmxLoadIndicator(this);"
class="oh-main__titlebar oh-main__titlebar--right"
>
<input type="text" name="nav_url" value="{{request.path}}" hidden>
<div class="oh-input-group oh-input__search-group" id="searchGroup">
<ion-icon
name="search-outline"
@@ -236,6 +237,9 @@
var data ={{last_filter|safe}}
for (const key in data) {
const field = form.elements[key];
if ({{empty_inputs|safe}}.includes(key)) {
continue
}
const value = data[key][0]; // Get the first element in the array
if (field) {

View File

@@ -110,6 +110,10 @@
$("div.oh-tabs").find(`{{active_target|safe}}`).click();
if (!$(".oh-tabs__tab--active").length) {
$("#{{view_id}}").find(".oh-tabs__tab").first().click()
}else{
setTimeout(() => {
$(".oh-tabs__tab--active").click();
}, 100);
}
{% endif %}
</script>

View File

@@ -1,23 +1,31 @@
<div id="{{dynamic_id}}">
{{field}}
</dic>
</div>
<script>
dynamic_value = $("#{{dynamic_id}} [name={{field.name}}]").val()
dynamic_value = {{field.initial|safe}}
$("#reload-field{{field.name}}{{view_id}}").find('input[name=dynamic_initial]').val({{field.initial|safe}})
$("#{{dynamic_id}} [name={{field.name}}]").val(dynamic_value).change()
if (dynamic_value != "dynamic_create"){
$("#{{dynamic_id}} [name={{field.name}}]").change()
}
$("#{{dynamic_id}} [name={{field.name}}]").change(function (e) {
values = $(this).val();
if (!values) {
values = ""
}
if (values == "dynamic_create") {
$("#modalButton{{field_tuple.0}}").click()
$("#modalButton{{field.name}}").click()
}else if (values.includes("dynamic_create")) {
let index = values.indexOf("dynamic_create");
values.splice(index, 1);
$(this).val(values).change();
$("#{{request.GET.view_id}} #modalButton{{field.name}}").parent().find('input[name=dynamic_initial]').val(values)
$("#{{request.GET.view_id}} #reload-field{{field.name}}{{request.GET.view_id}}").find('input[name=dynamic_initial]').val(values)
$("#{{request.GET.view_id}} #modalButton{{field.name}}").click()
$("#modalButton{{field.name}}").parent().find('input[name=dynamic_initial]').val(values)
$("#reload-field{{field.name}}{{request.GET.view_id}}").find('input[name=dynamic_initial]').val(values)
$("#modalButton{{field.name}}").click()
}else {
$("#modalButton{{field.name}}").parent().find('input[name=dynamic_initial]').val(values)
$("#reload-field{{field.name}}{{view_id}}").find('input[name=dynamic_initial]').val(values)
}
});

View File

@@ -71,7 +71,14 @@ class ReloadField(View):
parent_form = getattr(module, class_name)()
dynamic_cache = CACHE.get(request.session.session_key + "cbv" + reload_field)
onchange = CACHE.get(
request.session.session_key + "cbv" + reload_field + "onchange"
)
if not onchange:
onchange = ""
model: models.HorillaModel = dynamic_cache["model"]
value = dynamic_cache.get("value", "")
cache_field = dynamic_cache["dynamic_field"]
if cache_field != reload_field:
@@ -87,6 +94,11 @@ class ReloadField(View):
form_field = forms.ChoiceField
if isinstance(field, forms.ModelMultipleChoiceField):
form_field = forms.MultipleChoiceField
dynamic_initial = request.GET.get("dynamic_initial", [])
value = eval_validate(f"""[{dynamic_cache["value"]},{dynamic_initial}]""")
else:
if not value and self.request.GET.get("dynamic_initial"):
value = eval_validate(self.request.GET.get("dynamic_initial"))
parent_form.fields[cache_field] = form_field(
choices=choices,
@@ -96,11 +108,9 @@ class ReloadField(View):
parent_form.fields[cache_field].widget.option_template_name = (
"horilla_widgets/select_option.html",
)
dynamic_initial = request.GET.get("dynamic_initial", [])
parent_form.fields[cache_field].widget.attrs = field.widget.attrs
parent_form.fields[cache_field].initial = eval_validate(
f"""[{dynamic_cache["value"]},{dynamic_initial}]"""
)
parent_form.fields[cache_field].initial = value
parent_form.fields[cache_field].widget.attrs["onchange"] = onchange
field = parent_form[cache_field]
dynamic_id: str = get_short_uuid(4)
@@ -257,11 +267,16 @@ class LastAppliedFilter(View):
"""
Get method
"""
CACHE.set(
self.request.session.session_key + "last-applied-filter",
self.request.GET,
timeout=600,
nav_path = self.request.GET.get(
"nav_url",
)
if nav_path:
CACHE.set(
self.request.session.session_key + "last-applied-filter" + nav_path,
self.request.GET,
timeout=600,
)
return HttpResponse("success")