web-dev-qa-db-fra.com

Django: champ de liste dans le modèle?

Dans mon modèle, je veux un champ qui a une liste de triplets. par exemple. `[[1, 3, 4], [4, 2, 6], [8, 12, 3], [3, 3, 9]]. Existe-t-il un champ pouvant stocker ces données dans la base de données?

54
Karnivaurus

Vous pouvez le convertir en chaîne à l'aide de JSON et le stocker en tant que chaîne.

Par exemple, 

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

Vous pouvez ajouter une méthode dans votre classe pour la convertir automatiquement pour vous. 

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)

Si vous utilisez Django 1.9 et postgresql, il existe une nouvelle classe appelée JSONField, vous devriez plutôt l’utiliser. Voici un lien pour cela

Il y a une bonne discussion sur les JSON et les tableaux PostgreSQL sur youtube . Regardez-le, il contient de très bonnes informations. 

76
Ahmed

Si vous utilisez PostgreSQL, vous pouvez utiliser ArrayField avec un ArrayField imbriqué: https://docs.djangoproject.com/en/1.8/ref/contrib/postgres/fields/

De cette façon, la structure de données sera connue de la base de données sous-jacente. En outre, l'ORM apporte des fonctionnalités spéciales pour cela.

Notez que vous devrez cependant créer vous-même un index GIN (voir le lien ci-dessus, plus bas: https://docs.djangoproject.com/fr/1.8/ref/contrib/postgres/fields/#indexing- arrayfield ).

20
Risadinha

Si vous utilisez une version plus moderne de Django telle que 1.10 et que votre base de données est Postgres, il existe un nouveau ArrayField qu'il est préférable d'utiliser que Django-taggit ou d'autres alternatives, car il est natif du framework Django.

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,
    )
16
madmanick

Je pense que cela vous aidera.

 à partir de Django.db modèles d'importation 
 importation 

 classe ListField (models.TextField): 
 __metaclass__ = models.SubfieldBase 
 description = "Stocke une liste python" 

 def __init __ (self, * args, ** kwargs): 
 super (ListField, self) .__ init __ (* arguments, ** kwargs) 

 def to_python (self, value): 
 sinon valeur: 
 valeur = [] 

 si estinstance (valeur, liste): 
 valeur de retour 

 retourne ast.literal_eval (valeur) 

 def get_prep_value (self, value): 
 si la valeur est None: 
 valeur de retour 

 renvoyer unicode (valeur) 

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

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

Exemple : 

 >>> ListModel.objects.create (test_list = [[1,2,3], [2,3,4,4]]) 

 >>> ListModel.objects.get (id = 1) 

 >>> o = ListModel.objects.get (id = 1) 
 >>> o.id 
 1L 
> [1, 2, 3], [2, 3, 4, 4]] 
 >>> 
15
Prashant Gaur

Utilisez simplement un champ JSON fourni par ces packages tiers:

Dans ce cas, vous n'avez pas besoin de vous soucier de la sérialisation des valeurs de champ - cela se fera sous le capot.

J'espère que cela pourra aider.

7
alecxe

Vous pouvez aplatir la liste, puis stocker les valeurs dans un fichier CommaSeparatedIntegerField . Lorsque vous relisez la base de données, regroupez simplement les valeurs en trois.

Avertissement: selon la théorie de la normalisation de la base de données, il est préférable de ne pas stocker les collections dans des champs uniques; au lieu de cela, vous serez encouragé à stocker les valeurs de ces triplets dans leurs propres champs et à les lier via des clés étrangères. Dans le monde réel, cependant, cela est parfois trop lourd/lent.

1
RexE

Compte tenu de ma réputation actuelle, je n'ai pas la possibilité de commenter. J'ai donc choisi de répondre aux commentaires de référence pour l'exemple de code dans répondre par Prashant Gaur (merci, Gaur - cela a été utile!) - son exemple est pour python2 depuis python3 n'a pas 

unicode
méthode. 

Le remplacement ci-dessous pour la fonction 

get_prep_value (self, value):
devrait fonctionner avec Django fonctionnant avec python3 (j'utiliserai bientôt ce code - pas encore testé) . Notez cependant que je passe 
encoding = 'utf-8', errors = 'ignore'
paramètres à 
décoder()
et 
méthodes unicode ()
. Le codage doit correspondre à votre configuration Django settings.py et à sa transmission. 
erreurs = 'ignorer'
est facultatif (et peut entraîner une perte de données silencieuse au lieu d'une exception avec Django mal configuré dans de rares cas).

 import sys 

...

 def get_prep_value (self, value): 
 si la valeur est None: 
 valeur de retour 
 si sys.version_info [0]> = 3: 
 Si isinstance (out_data, tapez (b '')): 
 return value.decode (encoding = 'utf-8', errors = 'ignore') 
 autre:
 Si isinstance (out_data, tapez (b '')): 
 renvoyer unicode (valeur, encodage = 'utf-8', errors = 'ignorer') 
 retourne str (valeur) 
...


0
Oleg Artemev

Si vous utilisez Google App Engine ou MongoDB comme serveur principal et que vous utilisez la bibliothèque djangoappengine, il existe un ListField intégré qui fait exactement ce que vous voulez. En outre, il est facile d'interroger le champ de liste pour trouver tous les objets contenant un élément dans la liste.

0
speedplane

C'est un sujet assez ancien, mais comme il est renvoyé lors de la recherche du "champ de liste Django", je vais partager le code de champ de liste Django personnalisé que j'ai modifié pour fonctionner avec Python 3 et Django 2. Il prend désormais en charge l'interface d'administration sans utiliser eval (qui est une énorme faille de sécurité dans le code de Prashant Gaur).

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)
0
Yaroslav