Skip to content

Instantly share code, notes, and snippets.

@JakubDotPy
Forked from gauravvjn/admin.py
Last active October 8, 2021 14:10
Show Gist options
  • Save JakubDotPy/7a5d64d9a73113d0ec2700dba4112ff4 to your computer and use it in GitHub Desktop.
Save JakubDotPy/7a5d64d9a73113d0ec2700dba4112ff4 to your computer and use it in GitHub Desktop.
Use JSONField properties in Django admin filter Raw
"""
More details on the implementation and usage can be found at
https://www.pyscoop.com/django-jsonfield-attributes-in-admin-filter/
"""
from django.contrib import admin
from django.core.exceptions import ImproperlyConfigured
class JSONFieldFilter(admin.SimpleListFilter):
"""Base JSONFilter class to use by individual attribute filter classes.
e.g. data in JSON field
{
"name": "Jakub",
"company": "ZF",
"address": {
"city": "Pilsen",
"country": {"name": "Czechia", "code": "CZ"}
}
}
"""
model_json_field_name = None # name of the json field column in the model
json_data_property_name = None # name of one attribute from json data
def get_child_value_from_json_field_data(self, json_field_data):
key_list = self.json_data_property_name.split('__')
for key in key_list:
if isinstance(json_field_data, dict):
json_field_data = json_field_data[key]
return json_field_data
def lookups(self, request, model_admin):
"""
Returns a list of tuples.
The first element in each tuple is the coded value for the option that will appear in the URL query.
The 2nd element is the human-readable name for the option that will appear in the right sidebar.
"""
if self.model_json_field_name is None:
raise ImproperlyConfigured(f'Filter class {self.__class__.__name__} does not specify "model_json_field_name"')
if self.json_data_property_name is None:
raise ImproperlyConfigured(f'Filter class {self.__class__.__name__} does not specify "json_data_property_name"')
field_value_set = set()
for json_field_data in model_admin.model.objects.values_list(self.model_json_field_name, flat=True):
field_value_set.add(self.get_child_value_from_json_field_data(json_field_data))
return [(v, v) for v in field_value_set]
@staticmethod
def _retype_value(value):
"""Tries to convert strings to integers."""
if not value:
return value
try:
value = int(value)
except ValueError:
try:
value = float(value)
except ValueError:
return value
finally:
return value
def value(self):
return self._retype_value(super(JSONFieldFilter, self).value())
def queryset(self, request, queryset):
"""
Returns the filtered queryset based on the value provided in the query string & retrievable via `self.value()`
"""
if value := self.value():
json_field_query = {f'{self.model_json_field_name}__{self.json_data_property_name}': value}
return queryset.filter(**json_field_query)
else:
return queryset
class OsFilter(JSONFieldFilter):
"""Used for hardware_computerhardware filtering"""
model_json_field_name = 'os'
json_data_property_name = 'system' # property/field in json data
title = 'Os' # for admin sidebar
parameter_name = 'os_system'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment