Skip to content

Commit

Permalink
add functionality with queryset modifiers
Browse files Browse the repository at this point in the history
  • Loading branch information
PetrDlouhy committed Aug 2, 2024
1 parent 662edd5 commit 98b3356
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Changelog
1.4.0 (2024-08-02)
------------------

* added DashboardStats.queryset_modifiers to allow to modify queryset before it is used in chart, e.g. to add prefetch_related, filters or execute annotation functions. Fixed criteria are now deprecated in favour of queryset_modifiers.
* values in divided chart now are filtered by other criteria choices

1.3.1 (2024-04-12)
Expand Down
10 changes: 10 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ Requirements
* PostgreSQL (MySQL is experimental, other databases probably not working but PRs are welcome)
* ``simplejson`` for charts based on ``DecimalField`` values

=======
Warning
=======

The ``django-admin-charts`` application intended usage is mainly for system admins with access to Django admin interface.
The application is not intended to be used by untrusted users, as it is exposing some Django functionality to the user, especially in the chart configuration.

It has not been examined whether some malicious user with access to the charts could exploit the application to gain access to the system or data.


============
Installation
============
Expand Down
3 changes: 2 additions & 1 deletion admin_tools_stats/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ class DashboardStatsAdmin(admin.ModelAdmin):
"fields": (
"graph_key",
"graph_title",
("model_app_name", "model_name", "date_field_name"),
("model_app_name", "model_name", "date_field_name", "queryset_modifiers"),
("operation_field_name", "distinct"),
("user_field_name", "show_to_users"),
("allowed_type_operation_field_name", "type_operation_field_name"),
Expand Down Expand Up @@ -140,6 +140,7 @@ class DashboardStatsAdmin(admin.ModelAdmin):
"created_date",
"date_field_name",
"operation_field_name",
"queryset_modifiers",
"default_chart_type",
)
list_filter = [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Generated by Django 4.2.3 on 2023-10-12 11:26

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("admin_tools_stats", "0021_auto_20230210_1102"),
]

operations = [
migrations.AddField(
model_name="dashboardstats",
name="queryset_modifiers",
field=models.JSONField(
blank=True,
help_text=(
"Additional queryset modifiers in JSON format:<br>"
"<pre>"
"[<br>"
' {"filter": {"status": "active"}},<br>'
' {"exclude": {"status": "deleted"}}<br>'
' {"my_annotion_function": {}}<br>'
"]"
"</pre>"
"Ensure the format is a valid JSON array of objects."
"<br>"
"The format of the dict on each line is:"
"<br>"
'{"function_name": {"arg1": "value1", "arg2": "value2"}}'
"<br>"
"Where the arg/value pairs are the arguments to the function"
"that will be called on the queryset in order given by the list."
),
null=True,
verbose_name="Queryset modifiers",
),
),
migrations.AlterField(
model_name="dashboardstatscriteria",
name="criteria_fix_mapping",
field=models.JSONField(
blank=True,
help_text="DEPRECATED.<br>Use queryset modifiers instead<br>A JSON dictionary of key-value pairs that will be used for the criteria",
null=True,
verbose_name="fixed criteria / value",
),
),
]
37 changes: 36 additions & 1 deletion admin_tools_stats/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,10 @@ class DashboardStatsCriteria(models.Model):
null=True,
blank=True,
verbose_name=_("fixed criteria / value"),
help_text=_("a JSON dictionary of key-value pairs that will be used for the criteria"),
help_text=_(
"DEPRECATED.<br>Use queryset modifiers instead<br>"
"A JSON dictionary of key-value pairs that will be used for the criteria"
),
)
dynamic_criteria_field_name = models.CharField(
max_length=90,
Expand Down Expand Up @@ -315,6 +318,29 @@ class DashboardStats(models.Model):
"Can contain multiple fields divided by comma.",
),
)
queryset_modifiers = JSONField(
verbose_name=_("Queryset modifiers"),
null=True,
blank=True,
help_text=mark_safe(
"Additional queryset modifiers in JSON format:<br>"
"<pre>"
"[<br>"
' {"filter": {"status": "active"}},<br>'
' {"exclude": {"status": "deleted"}}<br>'
' {"my_annotion_function": {}}<br>'
"]"
"</pre>"
"Ensure the format is a valid JSON array of objects."
"<br>"
"The format of the dict on each line is:"
"<br>"
'{"function_name": {"arg1": "value1", "arg2": "value2"}}'
"<br>"
"Where the arg/value pairs are the arguments to the function"
"that will be called on the queryset in order given by the list."
),
)
distinct = models.BooleanField(
default=False,
null=False,
Expand Down Expand Up @@ -439,6 +465,14 @@ def get_model(self):

def get_queryset(self):
qs = self.get_model().objects
if self.queryset_modifiers:
for modifier in self.queryset_modifiers:
method_name = list(modifier.keys())[0]
method_args = modifier[method_name]
if isinstance(method_args, dict):
qs = getattr(qs, method_name)(**method_args)
else:
qs = getattr(qs, method_name)(*method_args)
return qs

def get_operation_field(self, operation):
Expand Down Expand Up @@ -535,6 +569,7 @@ def get_series_query_parameters(
criteria = m2m.criteria
# fixed mapping value passed info queryset_filters
if criteria.criteria_fix_mapping:
logger.warning("criteria_fix_mapping is deprecated. Use queryset_modifiers instead")
for key in criteria.criteria_fix_mapping:
# value => criteria.criteria_fix_mapping[key]
queryset_filters[key] = criteria.criteria_fix_mapping[key]
Expand Down

0 comments on commit 98b3356

Please sign in to comment.