25

I need to save a dictionary in a model's field. How do I do that?

For example I have this code:

def create_random_bill(self):
    name_chars = re.compile("[a-zA-Z0-9 -_]")
    bill_name = "".join(random.choice(name_chars for x in range(10)))
    rand_products = random.randint(1,100)
    for x in rand_products:
        bill_products = 
    new_bill = Bill.new(name=bill_name, date=datetime.date, products=bill_products)
    new_bill.save()

What do I write for "bill_products=" so it saves some random products, from my Product model to this bill?

This is the bill's model description:

class Bill(models.Model):
    name = models.CharField(max_length=255)
    date = models.DateTimeField(auto_now_add=True)
    products = models.ManyToManyField(Product, related_name="bills")

And also the product's model description:

class Product(models.Model):
    name = models.CharField(max_length=255)
    price = models.IntegerField()

If there's anything else i should add just leave a comment. Thanks!

Radu Gheorghiu
  • 20,049
  • 16
  • 72
  • 107

9 Answers9

21

I just discovered the django-jsonfield package, which

is a reusable Django field that allows you to store validated JSON in your model.

Looks like a viable option to achieve what you want.

ramiro
  • 878
  • 9
  • 20
  • 2
    Thanks for the update. It's good to know, even if this issue has been fixed a long time ago. – Radu Gheorghiu May 08 '13 at 12:09
  • 1
    This repo is not maintained since 2017. https://github.com/adamchainz/django-jsonfield is a fork of 2nd solution. Also, PostGres supports JSON fields natively: https://docs.djangoproject.com/en/dev/ref/contrib/postgres/fields/#jsonfield – Kangur Jul 24 '19 at 09:49
10

Using a custom field type is my preferred solution - I'd rather have a few lines of custom code than support an entire 3rd party library for a single field type. Tony Abou-Assaleh has a great solution, but won't work for newer versions of Django.

This is verified to work with Django 1.10.4

import json

from django.db import models
from django.core.serializers.json import DjangoJSONEncoder


class JSONField(models.TextField):
    """
    JSONField is a generic textfield that neatly serializes/unserializes
    JSON objects seamlessly.
    Django snippet #1478

    example:
        class Page(models.Model):
            data = JSONField(blank=True, null=True)


        page = Page.objects.get(pk=5)
        page.data = {'title': 'test', 'type': 3}
        page.save()
    """

    def to_python(self, value):
        if value == "":
            return None

        try:
            if isinstance(value, str):
                return json.loads(value)
        except ValueError:
            pass
        return value

    def from_db_value(self, value, *args):
        return self.to_python(value)

    def get_db_prep_save(self, value, *args, **kwargs):
        if value == "":
            return None
        if isinstance(value, dict):
            value = json.dumps(value, cls=DjangoJSONEncoder)
        return value
Rico
  • 5,692
  • 8
  • 46
  • 63
  • 4
    This does not handle serialization (`manage.py dumpdata`), does not handle lookups in db, does not handle editing in Django adming (because representation shown is that of serialized dict), etc. Maybe in the end using a 3rd party lib saves you from running into errors. – Kangur Nov 14 '19 at 16:23
10

One convenient way to store a JSON representation in a model is to use a custom field type:

class JSONField(models.TextField):
    """
    JSONField is a generic textfield that neatly serializes/unserializes
    JSON objects seamlessly.
    Django snippet #1478

    example:
        class Page(models.Model):
            data = JSONField(blank=True, null=True)


        page = Page.objects.get(pk=5)
        page.data = {'title': 'test', 'type': 3}
        page.save()
    """

    __metaclass__ = models.SubfieldBase

    def to_python(self, value):
        if value == "":
            return None

        try:
            if isinstance(value, basestring):
                return json.loads(value)
        except ValueError:
            pass
        return value

    def get_db_prep_save(self, value, *args, **kwargs):
        if value == "":
            return None
        if isinstance(value, dict):
            value = json.dumps(value, cls=DjangoJSONEncoder)
        return super(JSONField, self).get_db_prep_save(value, *args, **kwargs)

I saved this utils/fields.py and in my model from utils.fields import JSONField. There are many more goodies in the django-annoying app, which is where this snippet came from.

Tony Abou-Assaleh
  • 3,000
  • 2
  • 25
  • 37
6

Probably the cleanest thing to do would be to create another "Products" table and have a many-to-many relationship. (See here: https://docs.djangoproject.com/en/dev/topics/db/models/#many-to-many-relationships . In the docs they use the example of a pizza having many toppings.)

The other option would be to serialize your bill_products. In that case, you'd do something like:

bill_products = json.dumps([rand_products])

This would be outside of the for loop (although, in your example above, rand_products is only a single value, so you'll need to fix that).

gdw2
  • 7,558
  • 4
  • 46
  • 49
  • If you could provide an example it would be very nice. I'm afraid I didn't understand how exactly to translate a many-to-many relationship to my code. Am I on the right idea path with saving an array with product names in that field? Or could I do it in a simpler approach? – Radu Gheorghiu Mar 14 '12 at 17:23
  • @reos You are on on the right track. Here is a full example: https://www.djangoproject.com/documentation/0_91/models/many_to_many/ – gdw2 Mar 14 '12 at 18:26
4

If postgres is your backend, consider the hstore field which has native support from django

wjin
  • 954
  • 1
  • 8
  • 13
3

You can use serialization/deserialization from pickle module:

http://docs.python.org/library/pickle.html

Alejandro Veintimilla
  • 10,743
  • 23
  • 91
  • 180
2

I think that I would create the field as models.CharField() and then encode the dictionary as a JSON string and save that string into the database. Then you can decode the JSON string back into a dictionary when you read it out.

brian buck
  • 3,284
  • 3
  • 23
  • 24
1

If using PostGres you can store it in natively supported JSON field: https://docs.djangoproject.com/en/dev/ref/contrib/postgres/fields/#jsonfield

Otherwise I'd recommend @ramiro answer with 3rd party lib https://stackoverflow.com/a/16437627/803174

Kangur
  • 7,823
  • 3
  • 30
  • 32
0

according to Django doc you can use :

from django.contrib.postgres.fields import JSONField
from django.db import models

class Dog(models.Model):
    name = models.CharField(max_length=200)
    data = JSONField()

    def __str__(self):
        return self.name

and create with this :

Dog.objects.create(name='Rufus', data={
     'breed': 'labrador',
     'owner': {
         'name': 'Bob',
         'other_pets': [{
             'name': 'Fishy',
         }],
     },
})

I hope this could help you.

hassanzadeh.sd
  • 3,091
  • 1
  • 17
  • 26