Search code examples
djangodjango-modelsdjango-database

How to store an object in a django model?


I am receiving an object from the frontend as a parameter alongside with other parameters. How can I store that object into my model?

I've tried JSONfield but not sure if its the best way to do it. This will be a tracking service like Mixpanel

this is roughly the object looks like in the front end:

myObj = {
context: ["Home Page"]
dropDown: "navigated to main screen"
side: "creator-side"
withSelection: false
}

and I want to have it in Django:

@reversion.register()
class TrackedEvents(models.Model):
    ...
    event_name = models.TextField(max_length=250, null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    myObj = { what to add here }

I want to be able to filter the events based on the properties in the object on admin panel.


Solution

  • If you're using PostgreSQL, you can take advantage of the Django field type JSONField to make a model field that stores a structured JSON object. Then you can add a custom filter to query on JSON in the Django admin.

    admin.py

    from django.contrib.postgres.fields import JSONField
    
    class TrackedEvents(models.Model):
        event_name = models.TextField(max_length=250, null=True, blank=True)
        created_at = models.DateTimeField(auto_now_add=True)
        detail = JSONField()
    

    Custom Filter

    from django.contrib.admin import SimpleListFilter
    
    class JSONFieldFilter(SimpleListFilter):
        def __init__(self, *args, **kwargs):
            super(JSONFieldFilter, self).__init__(*args, **kwargs)
            assert hasattr(self, 'title'), (
                'Class {} missing "title" attribute'.format(self.__class__.__name__)
            )
            assert hasattr(self, 'parameter_name'), (
                'Class {} missing "parameter_name" attribute'.format(self.__class__.__name__)
            )
            assert hasattr(self, 'json_field_name'), (
                'Class {} missing "json_field_name" attribute'.format(self.__class__.__name__)
            )
            assert hasattr(self, 'json_field_property_name'), (
                'Class {} missing "json_field_property_name" attribute'.format(self.__class__.__name__)
            )
    
        def lookups(self, request, model_admin):
            values_list = map(
                lambda data: data[self.json_field_property_name],
                model_admin.model.objects.values_list(self.json_field_name, flat=True)
            )
            return [(v, v) for v in set(values_list)]
    
        def queryset(self, request, queryset):
            if self.value():
                key = "{}__{}".format(self.json_field_name, self.json_field_property_name)
                return queryset.filter(**{key: self.value()})
            return queryset
    

    So you can use the custom filter like this (e.g. for side property) in your admin.py.

    class SideFilter(JSONFieldFilter):
        title = 'Side'
        parameter_name = 'side'
        json_field_name = 'detail'
        json_field_property_name = 'side'
    
    
     class TrackedEventsAdmin(admin.ModelAdmin):
        list_filter = [SideFilter]