[Django]-List field in model?

132👍

You can convert it into string by using JSON and store it as string.

For example,

In [3]: json.dumps([[1, 3, 4], [4, 2, 6], [8, 12, 3], [3, 3, 9]])

Out[3]: '[[1, 3, 4], [4, 2, 6], [8, 12, 3], [3, 3, 9]]'

You can add a method into your class to convert it automatically for you.

import json


class Foobar(models.Model):
    foo = models.CharField(max_length=200)

    def set_foo(self, x):
        self.foo = json.dumps(x)

    def get_foo(self):
        return json.loads(self.foo)

If you’re using Django 1.9 or above, and you use postgresql, there is a new class called JSONField, you should use it instead. Here is a link to it

There is a good talk about PostgreSQL JSONs and Arrays on youtube. Watch it, it has very good information.

👤Ahmed

53👍

If you’re on Django 1.10 or newer AND Postgres as your database, you can use ArrayField. It’s better to use than django-taggit or other alternatives, as it’s native to the Django framework.
https://docs.djangoproject.com/en/3.1/ref/contrib/postgres/fields/#arrayfield

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

class ChessBoard(models.Model):
    board = ArrayField(
        ArrayField(
            models.CharField(max_length=10, blank=True),
            size=8,
        ),
        size=8,
    )

If you’re on Django 3.1 or newer they’ve added support for JSONField with most database backends (MariaDB 10.2.7+, MySQL 5.7.8+, Oracle, PostgreSQL, and SQLite 3.9.0+). You can use this to store your Array!

https://docs.djangoproject.com/en/3.1/ref/models/fields/#jsonfield

from django.db import models

class ChessBoard(models.Model):
    list_of_pieces = models.JSONField()

25👍

If you are using PostgreSQL, you can use ArrayField with a nested ArrayField: https://docs.djangoproject.com/en/2.2/ref/contrib/postgres/fields/

This way, the data structure will be known to the underlying database. Also, the ORM brings special functionality for it.

Note that you will have to create a GIN index by yourself, though (see the above link, further down: https://docs.djangoproject.com/en/2.2/ref/contrib/postgres/fields/#indexing-arrayfield).

(Edit: updated links to newest Django LTS, this feature exists at least since 1.8.)

21👍

I think it will help you.

from django.db import models
import ast

class ListField(models.TextField):
    __metaclass__ = models.SubfieldBase
    description = "Stores a python list"

    def __init__(self, *args, **kwargs):
        super(ListField, self).__init__(*args, **kwargs)

    def to_python(self, value):
        if not value:
            value = []

        if isinstance(value, list):
            return value

        return ast.literal_eval(value)

    def get_prep_value(self, value):
        if value is None:
            return value

        return unicode(value)

    def value_to_string(self, obj):
        value = self._get_val_from_obj(obj)
        return self.get_db_prep_value(value)

class ListModel(models.Model):
    test_list = ListField()

Example :

>>> ListModel.objects.create(test_list= [[1,2,3], [2,3,4,4]])
<ListModel: ListModel object>
>>> ListModel.objects.get(id=1)
<ListModel: ListModel object>
>>> o = ListModel.objects.get(id=1)
>>> o.id
1L
>>> o.test_list
[[1, 2, 3], [2, 3, 4, 4]]
>>> 

9👍

It’s quite an old topic, but since it is returned when searching for “django list field” I’ll share the custom django list field code I modified to work with Python 3 and Django 2. It supports the admin interface now and not uses eval (which is a huge security breach in Prashant Gaur’s code).

from django.db import models
from typing import Iterable

class ListField(models.TextField):
    """
    A custom Django field to represent lists as comma separated strings
    """

    def __init__(self, *args, **kwargs):
        self.token = kwargs.pop('token', ',')
        super().__init__(*args, **kwargs)

    def deconstruct(self):
        name, path, args, kwargs = super().deconstruct()
        kwargs['token'] = self.token
        return name, path, args, kwargs

    def to_python(self, value):

        class SubList(list):
            def __init__(self, token, *args):
                self.token = token
                super().__init__(*args)

            def __str__(self):
                return self.token.join(self)

        if isinstance(value, list):
            return value
        if value is None:
            return SubList(self.token)
        return SubList(self.token, value.split(self.token))

    def from_db_value(self, value, expression, connection):
        return self.to_python(value)

    def get_prep_value(self, value):
        if not value:
            return
        assert(isinstance(value, Iterable))
        return self.token.join(value)

    def value_to_string(self, obj):
        value = self.value_from_object(obj)
        return self.get_prep_value(value)

7👍

Just use a JSON field that these third-party packages provide:

In this case, you don’t need to care about the field value serialization – it’ll happen under-the-hood.

👤alecxe

2👍

With my current reputation I have no ability to comment, so I choose answer referencing comments for sample code in reply by Prashant Gaur (thanks, Gaur – this was helpful!) – his sample is for python2, since python3 has no

unicode

method.

The replacement below for function

get_prep_value(self, value):

should work with Django running with python3 (I’ll use this code soon – yet not tested).
Note, though, that I’m passing

encoding='utf-8', errors='ignore'

parameters to

decode()

and

unicode() methods

. Encoding should match your Django settings.py configuration and passing

errors='ignore'

is optional (and may result in silent data loss instead of exception whith misconfigured django in rare cases).

import sys

...

    def get_prep_value(self, value):
        if value is None:
            return value
        if sys.version_info[0] >= 3:
            if isinstance(out_data, type(b'')):
                return value.decode(encoding='utf-8', errors='ignore')
        else:
            if isinstance(out_data, type(b'')):
                return unicode(value, encoding='utf-8', errors='ignore')
        return str(value)
...


1👍

You can flatten the list and then store the values to a CommaSeparatedIntegerField. When you read back from the database, just group the values back into threes.

Disclaimer: according to database normalization theory, it is better not to store collections in single fields; instead you would be encouraged to store the values in those triplets in their own fields and link them via foreign keys. In the real world, though, sometimes that is too cumbersome/slow.

👤RexE

0👍

If you are using Google App Engine or MongoDB as your backend, and you are using the djangoappengine library, there is a built in ListField that does exactly what you want. Further, it’s easy to query the Listfield to find all objects that contain an element in the list.

Leave a comment