Last active
August 17, 2023 20:27
-
-
Save gauravvjn/b264562ba66067f85da7a55063cbfa64 to your computer and use it in GitHub Desktop.
Use JSONField properties in Django admin filter Raw
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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": "Gaurav", | |
"company": "pyscoop", | |
"address": { | |
"city": "Jaipur", | |
"country": {"name": "India", "code": "IN"} | |
} | |
} | |
""" | |
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] | |
def queryset(self, request, queryset): | |
""" | |
Returns the filtered queryset based on the value provided in the query string & retrievable via `self.value()` | |
""" | |
if self.value(): | |
json_field_query = {f'{self.model_json_field_name}__{self.json_data_property_name}': self.value()} | |
return queryset.filter(**json_field_query) | |
else: | |
return queryset | |
class CompanyFilter(JSONFieldFilter): | |
model_json_field_name = 'jsonfield' | |
json_data_property_name = 'company' # property/field in json data | |
title = 'Company' # for admin sidebar | |
parameter_name = 'js_company' # Prefixing with `js_` because one might have another column named `company`, will be used in the URL query |
Doesn't work with integers.
See my fork with improvements.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is awesome, thanks for sharing! 👍