List field in model?

DjangoDjango Orm

Django Problem Overview


In my model, I want a field that has a list of triplets. e.g. [[1, 3, 4], [4, 2, 6], [8, 12, 3], [3, 3, 9]]. Is there a field that can store this data in the database?

Django Solutions


Solution 1 - Django

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.

Solution 2 - Django

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()

Solution 3 - Django

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.)

Solution 4 - Django

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]]
>>> 

Solution 5 - Django

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.

Solution 6 - Django

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)

Solution 7 - Django

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)

...


Solution 8 - Django

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.

Solution 9 - Django

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKarnivaurusView Question on Stackoverflow
Solution 1 - DjangoAhmedView Answer on Stackoverflow
Solution 2 - DjangomadmanickView Answer on Stackoverflow
Solution 3 - DjangoRisadinhaView Answer on Stackoverflow
Solution 4 - DjangoPrashant GaurView Answer on Stackoverflow
Solution 5 - DjangoalecxeView Answer on Stackoverflow
Solution 6 - DjangoYaroslavView Answer on Stackoverflow
Solution 7 - DjangoOleg ArtemevView Answer on Stackoverflow
Solution 8 - DjangoRexEView Answer on Stackoverflow
Solution 9 - DjangospeedplaneView Answer on Stackoverflow