web-dev-qa-db-fra.com

Quel est le moyen le plus efficace de stocker une liste dans les modèles Django?

Actuellement, j'ai beaucoup d'objets python dans mon code similaires à ceux-ci:

class MyClass():
  def __init__(self, name, friends):
      self.myName = name
      self.myFriends = [str(x) for x in friends]

Maintenant, je veux transformer ceci en un modèle Django, où self.myName est un champ de chaîne et self.myFriends est une liste de chaînes.

from Django.db import models

class myDjangoModelClass():
    myName = models.CharField(max_length=64)
    myFriends = ??? # what goes here?

Comme la liste est une structure de données commune en python, je m'attendais à ce qu'il y ait un champ de modèle Django). Je sais que je peux utiliser une relation ManyToMany ou OneToMany, mais j'espérais pour éviter cette indirection supplémentaire dans le code.

Modifier:

J'ai ajouté ceci question connexe , que les gens peuvent trouver utile.

119
grieve

Cette relation ne serait-elle pas mieux exprimée en tant que relation de clé étrangère un-à-plusieurs avec une table Friends? Je comprends que myFriends ne sont que des chaînes, mais je pense qu’une meilleure conception consisterait à créer un modèle Friend et à avoir MyClass à contenir une relation de clé étrangère avec la table résultante.

61
Andrew Hare

"L'optimisation prématurée est la racine de tout mal."

Avec cela fermement en tête, faisons ceci! Une fois que vos applications ont atteint un certain point, la dénormalisation des données est très courante. Fait correctement, il peut économiser de nombreuses recherches de base de données coûteuses au détriment d'un peu plus de ménage.

Pour renvoyer un list de noms d'amis, nous devons créer une classe personnalisée Django) qui renverra une liste lors de l'accès.

David Cramer a publié un guide sur la création d'un SeperatedValueField sur son blog. Voici le code:

from Django.db import models

class SeparatedValuesField(models.TextField):
    __metaclass__ = models.SubfieldBase

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

    def to_python(self, value):
        if not value: return
        if isinstance(value, list):
            return value
        return value.split(self.token)

    def get_db_prep_value(self, value):
        if not value: return
        assert(isinstance(value, list) or isinstance(value, Tuple))
        return self.token.join([unicode(s) for s in value])

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

La logique de ce code traite de la sérialisation et de la désérialisation des valeurs de la base de données à Python et vice-versa. Vous pouvez désormais importer et utiliser facilement notre champ personnalisé dans la classe de modèle:

from Django.db import models
from custom.fields import SeparatedValuesField 

class Person(models.Model):
    name = models.CharField(max_length=64)
    friends = SeparatedValuesField()
110
jb.

Un moyen simple de stocker une liste dans Django consiste simplement à la convertir en chaîne JSON, puis à l'enregistrer en tant que texte dans le modèle. Vous pouvez ensuite récupérer la liste en convertissant le fichier (JSON). chaîne en arrière dans une liste python. Voici comment:

La "liste" serait stockée dans votre modèle Django comme ceci:

class MyModel(models.Model):
    myList = models.TextField(null=True) # JSON-serialized (text) version of your list

Dans votre vue/code du contrôleur:

Stocker la liste dans la base de données:

import simplejson as json # this would be just 'import json' in Python 2.7 and later
...
...

myModel = MyModel()
listIWantToStore = [1,2,3,4,5,'hello']
myModel.myList = json.dumps(listIWantToStore)
myModel.save()

Récupération de la liste de la base de données:

jsonDec = json.decoder.JSONDecoder()
myPythonList = jsonDec.decode(myModel.myList)

conceptuellement, voici ce qui se passe:

>>> myList = [1,2,3,4,5,'hello']
>>> import simplejson as json
>>> myJsonList = json.dumps(myList)
>>> myJsonList
'[1, 2, 3, 4, 5, "hello"]'
>>> myJsonList.__class__
<type 'str'>
>>> jsonDec = json.decoder.JSONDecoder()
>>> myPythonList = jsonDec.decode(myJsonList)
>>> myPythonList
[1, 2, 3, 4, 5, u'hello']
>>> myPythonList.__class__
<type 'list'>
32
mindthief

Si vous utilisez Django> = 1.9 avec Postgres, vous pouvez utiliser ArrayField avantages

Un champ pour stocker des listes de données. La plupart des types de champs peuvent être utilisés, vous passez simplement une autre instance de champ comme base_field. Vous pouvez également spécifier une taille. ArrayField peut être imbriqué pour stocker des tableaux multidimensionnels.

Il est également possible d'imbriquer des champs de tableau:

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

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

Comme @ thane-brimhall l'a mentionné, il est également possible d'interroger directement des éléments. Documentation référence

17
wolendranh

Comme il s’agit d’une question ancienne et que les techniques Django doivent avoir changé considérablement depuis, cette réponse correspond à Django version 1.4, et s’applique très probablement à la version 1.5.

Django utilise par défaut des bases de données relationnelles; vous devriez les utiliser. Mappez des amitiés sur des relations de base de données (contraintes de clé étrangère) à l'aide de ManyToManyField. Cela vous permet d'utiliser RelatedManagers pour les listes d'amis, qui utilisent des ensembles de requêtes intelligents. Vous pouvez utiliser toutes les méthodes disponibles telles que filter ou values_list.

Utilisation de ManyToManyField relations et propriétés:

class MyDjangoClass(models.Model):
    name = models.CharField(...)
    friends = models.ManyToManyField("self")

    @property
    def friendlist(self):
        # Watch for large querysets: it loads everything in memory
        return list(self.friends.all())

Vous pouvez accéder à la liste d'amis d'un utilisateur de cette manière:

joseph = MyDjangoClass.objects.get(name="Joseph")
friends_of_joseph = joseph.friendlist

Notez cependant que ces relations sont symétriques: si Joseph est un ami de Bob, alors Bob est un ami de Joseph.

15
sleblanc
class Course(models.Model):
   name = models.CharField(max_length=256)
   students = models.ManyToManyField(Student)

class Student(models.Model):
   first_name = models.CharField(max_length=256)
   student_number = models.CharField(max_length=128)
   # other fields, etc...

   friends = models.ManyToManyField('self')
9
Andriy Drozdyuk

Si vous utilisez postgres, vous pouvez utiliser quelque chose comme ceci:

class ChessBoard(models.Model):

    board = ArrayField(
        ArrayField(
            models.CharField(max_length=10, blank=True),
            size=8,
        ),
        size=8,
    )

si vous avez besoin de plus de détails, vous pouvez lire le lien ci-dessous: https://docs.djangoproject.com/pt-br/1.9/ref/contrib/postgres/fields/

6
Marcos Souza

Rappelez-vous que cela doit éventuellement se retrouver dans une base de données relationnelle. Donc, en utilisant des relations vraiment est la manière habituelle de résoudre ce problème. Si vous souhaitez absolument stocker une liste dans l'objet même, vous pouvez le séparer par des virgules, par exemple, et le stocker dans une chaîne, puis fournir des fonctions d'accès permettant de scinder la chaîne en une liste. Avec cela, vous serez limité à un nombre maximal de chaînes et vous perdrez des requêtes efficaces.

6
Martin v. Löwis

Vous pouvez stocker pratiquement n'importe quel objet en utilisant un Django Pickle Field, comme cet extrait de code:

http://www.djangosnippets.org/snippets/513/

4
Harold

Stocker une liste de chaînes dans Django model:

class Bar(models.Model):
    foo = models.TextField(blank=True)

    def set_list(self, element):
        if self.foo:
            self.foo = self.foo + "," + element
        else:
            self.foo = element

    def get_list(self):
        if self.foo:
            return self.foo.split(",")
        else:
            None

et vous pouvez l'appeler comme ça:

bars = Bar()
bars.set_list("str1")
bars.set_list("str2")
list = bars.get_list()
if list is not None:
    for bar in list:
        print bar
else:
    print "List is empty."      
3
Ahtisham

L'utilisation d'une relation un-à-plusieurs (FK de Friend à la classe parente) rendra votre application plus évolutive (vous pourrez étendre trivialement l'objet Friend avec des attributs supplémentaires allant au-delà du simple nom). Et donc c'est la meilleure façon

1
Guard