How to store a dictionary on a Django Model?

70,748

Solution 1

If it's really dictionary like arbitrary data you're looking for you can probably use a two-level setup with one model that's a container and another model that's key-value pairs. You'd create an instance of the container, create each of the key-value instances, and associate the set of key-value instances with the container instance. Something like:

class Dicty(models.Model):
    name      = models.CharField(max_length=50)

class KeyVal(models.Model):
    container = models.ForeignKey(Dicty, db_index=True)
    key       = models.CharField(max_length=240, db_index=True)
    value     = models.CharField(max_length=240, db_index=True)

It's not pretty, but it'll let you access/search the innards of the dictionary using the DB whereas a pickle/serialize solution will not.

Solution 2

If you don't need to query by any of this extra data, then you can store it as a serialized dictionary. Use repr to turn the dictionary into a string, and eval to turn the string back into a dictionary. Take care with eval that there's no user data in the dictionary, or use a safe_eval implementation.

For example, in the create and update methods of your views, you can add:

if isinstance(request.data, dict) == False:
    req_data = request.data.dict().copy()
else:
    req_data = request.data.copy()

dict_key = 'request_parameter_that_has_a_dict_inside'
if dict_key in req_data.keys() and isinstance(req_data[dict_key], dict):
    req_data[dict_key] = repr(req_data[dict_key])

Solution 3

Another clean and fast solution can be found here: https://github.com/bradjasper/django-jsonfield

For convenience I copied the simple instructions.

Install

pip install jsonfield

Usage

from django.db import models
from jsonfield import JSONField

class MyModel(models.Model):
    json = JSONField()

Solution 4

I came to this post by google's 4rth result to "django store object"

A little bit late, but django-picklefield looks like good solution to me.

Example from doc:

To use, just define a field in your model:

>>> from picklefield.fields import PickledObjectField
>>> class SomeObject(models.Model):
>>>     args = PickledObjectField()

and assign whatever you like (as long as it's picklable) to the field:

>>> obj = SomeObject()
>>> obj.args = ['fancy', {'objects': 'inside'}]
>>> obj.save()

Solution 5

As Ned answered, you won't be able to query "some data" if you use the dictionary approach.

If you still need to store dictionaries then the best approach, by far, is the PickleField class documented in Marty Alchin's new book Pro Django. This method uses Python class properties to pickle/unpickle a python object, only on demand, that is stored in a model field.

The basics of this approach is to use django's contibute_to_class method to dynamically add a new field to your model and uses getattr/setattr to do the serializing on demand.

One of the few online examples I could find that is similar is this definition of a JSONField.

Share:
70,748
AticusFinch
Author by

AticusFinch

Updated on July 08, 2022

Comments

  • AticusFinch
    AticusFinch almost 2 years

    I need to store some data in a Django model. These data are not equal to all instances of the model.

    At first I thought about subclassing the model, but I’m trying to keep the application flexible. If I use subclasses, I’ll need to create a whole class each time I need a new kind of object, and that’s no good. I’ll also end up with a lot of subclasses only to store a pair of extra fields.

    I really feel that a dictionary would be the best approach, but there’s nothing in the Django documentation about storing a dictionary in a Django model (or I can’t find it).

    Any clues?

  • Filip Dupanović
    Filip Dupanović almost 14 years
    The only downside is that an additional DB query will ensue
  • Nick Perkins
    Nick Perkins almost 13 years
    Another downside is that you have exactly one "level" of data, you can't create multi-level complex JSON-style data. ( but good idea nonetheless )
  • michel.iamit
    michel.iamit about 10 years
    Found a nice extension of this solution: djangosnippets.org/snippets/2451 this guy extended the dictionary to all pythonic dictionary functions
  • Micah Walter
    Micah Walter almost 10 years
    So how does this get called?
  • Anthony Manning-Franklin
    Anthony Manning-Franklin about 7 years
    @NickPerkins You could make it recursive by having Dicty contain a field parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True). Any dict without a parent is a top level object. Tadaaa! Yes it would be nice if value pointed to another dict rather than using a special field but ¯_(ツ)_/¯... Alternatively on KeyVal add child = models.ForeignKey('Dicty', models.CASCADE, null=True, blank=True)
  • Devendra Bhat
    Devendra Bhat about 5 years
    It is a nice way to use but it does not support dictionary validation by default. So if I send an int or a string it directly stores that data.
  • Dimitrios Mistriotis
    Dimitrios Mistriotis over 4 years
    from django.contrib.postgres import fields for PostgreSQL
  • Odasaku
    Odasaku over 2 years
    To explain in short, first you change the dict to JSON, then you change it to a string using json.loads and json.dumps respectively, and save that in the db. Finally when you retrieve the data you read it as a dict, right?
  • geogeo
    geogeo almost 2 years
    JSONField is available directly from django.db.models: from django.db.models import JSONField Usage details in docs: docs.djangoproject.com/en/4.0/ref/models/fields/…