web-dev-qa-db-fra.com

Comment stocker un dictionnaire sur un modèle Django?

J'ai besoin de stocker des données dans un modèle Django. Ces données ne sont pas égales à toutes les instances du modèle.

Au début, j'ai pensé à sous-classer le modèle, mais j'essaie de garder l'application flexible. Si j'utilise des sous-classes, je devrai créer une classe entière chaque fois que j'aurai besoin d'un nouveau type d'objet, et ce n'est pas bon. Je vais également me retrouver avec beaucoup de sous-classes uniquement pour stocker une paire de champs supplémentaires.

Je pense vraiment qu'un dictionnaire serait la meilleure approche, mais il n'y a rien dans la documentation Django sur le stockage d'un dictionnaire dans un modèle Django (ou je peux '' t le trouver).

Des indices?

50
AticusFinch

Si c'est vraiment un dictionnaire comme des données arbitraires que vous recherchez, vous pouvez probablement utiliser une configuration à deux niveaux avec un modèle qui est un conteneur et un autre modèle qui est des paires clé-valeur. Vous devez créer une instance du conteneur, créer chacune des instances de valeur-clé et associer l'ensemble d'instances de valeur-clé à l'instance de conteneur. Quelque chose comme:

class Dicty(models.Model):
    name      = models.CharField(max_length=50)

class KeyVal(models.Model):
    container = models.ForeignKey(Dicty, db_index=True)
    key       = models.CharField(max_length=240, db_index=True)
    value     = models.CharField(max_length=240, db_index=True)

Ce n'est pas joli, mais cela vous permettra d'accéder/de rechercher les entrailles du dictionnaire à l'aide de la base de données, contrairement à une solution de pickle/sérialisation.

37
Parand

Si vous n'avez pas besoin d'interroger par l'une de ces données supplémentaires, vous pouvez la stocker sous la forme d'un dictionnaire sérialisé. Utilisez repr pour transformer le dictionnaire en chaîne et eval pour reconvertir la chaîne en dictionnaire. Faites attention avec eval qu'il n'y ait pas de données utilisateur dans le dictionnaire, ou utilisez un safe_eval la mise en oeuvre.

Par exemple, dans les méthodes create et update de votre views, vous pouvez ajouter:

if isinstance(request.data, dict) == False:
    req_data = request.data.dict().copy()
else:
    req_data = request.data.copy()

dict_key = 'request_parameter_that_has_a_dict_inside'
if dict_key in req_data.keys() and isinstance(req_data[dict_key], dict):
    req_data[dict_key] = repr(req_data[dict_key])
15
Ned Batchelder

Je suis venu à ce poste par le 4ème résultat de google à "Django store object"

Un peu tard, mais Django-picklefield me semble une bonne solution.

Exemple de doc:

Pour l'utiliser, définissez simplement un champ dans votre modèle:

>>> from picklefield.fields import PickledObjectField
>>> class SomeObject(models.Model):
>>>     args = PickledObjectField()

et attribuez ce que vous voulez (tant qu'il est picklable) au champ:

>>> obj = SomeObject()
>>> obj.args = ['fancy', {'objects': 'inside'}]
>>> obj.save()
11
Stéphane

Une autre solution propre et rapide peut être trouvée ici: https://github.com/bradjasper/Django-jsonfield

Pour plus de commodité, j'ai copié les instructions simples.

Installer

pip install jsonfield

Utilisation

from Django.db import models
from jsonfield import JSONField

class MyModel(models.Model):
    json = JSONField()
11
odedfos

Comme Ned a répondu, vous ne pourrez pas interroger "certaines données" si vous utilisez l'approche par dictionnaire.

Si vous avez encore besoin de stocker des dictionnaires, la meilleure approche est de loin la classe PickleField documentée dans le nouveau livre de Marty Alchin Pro Django . Cette méthode utilise les propriétés de classe Python pour décaper/décaper un objet python, uniquement à la demande, qui est stocké dans un champ de modèle).

Les bases de cette approche sont d'utiliser le Django contibute_to_class méthode pour ajouter dynamiquement un nouveau champ à votre modèle et utilise getattr/setattr pour effectuer la sérialisation à la demande.

L'un des rares exemples en ligne que j'ai pu trouver qui est similaire est cette définition d'un JSONField .

7
Van Gale

Je ne suis pas sûr de la nature du problème que vous essayez de résoudre, mais cela ressemble étrangement à BigTable Expando de Google App Engine .

Expandos vous permet de spécifier et de stocker des champs supplémentaires sur une instance d'objet basée sur une base de données lors de l'exécution. Pour citer des documents:

import datetime
from google.appengine.ext import db

class Song(db.Expando):
  title = db.StringProperty()

crazy = Song(title='Crazy like a diamond',
             author='Lucy Sky',
             publish_date='yesterday',
             rating=5.0)

crazy.last_minute_note=db.Text('Get a train to the station.')

Google App Engine prend actuellement en charge à la fois Python et le cadre Django. Cela pourrait valoir la peine d'être examiné si c'est la meilleure façon d'exprimer vos modèles).

Les modèles de bases de données relationnelles traditionnels ne disposent pas de ce type de flexibilité d'ajout de colonnes. Si vos types de données sont assez simples, vous pouvez rompre avec la philosophie traditionnelle du SGBDR et pirater les valeurs dans une seule colonne via la sérialisation comme @ Ned Batchelder propose; cependant, si vous avez pour utiliser un SGBDR, Django est probablement la voie à suivre. Notamment, il créera ne clé étrangère un-à-un relation pour chaque niveau de dérivation.

5
cdleary

Être "pas égal à toutes les instances du modèle" me semble être une bonne correspondance avec une "base de données sans schéma". CouchDB est l'enfant de l'affiche pour cette approche et vous pourriez envisager cela.

Dans un projet, j'ai déplacé plusieurs tables qui ne jouaient jamais très bien avec l'ORM Django sur CouchDB et j'en suis très content. J'utilise couchdb-python sans l'un des modules CouchDB spécifiques à Django. Une description du modèle de données peut être trouvée ici . Le passage de cinq "modèles" dans Django à 3 "modèles" dans Django et une "base de données" CouchDB ont en fait légèrement réduit le nombre total de lignes de code dans mon application.

3
max

Je suis d'accord que vous devez éviter de bourrer des données autrement structurées dans une seule colonne. Mais si vous devez le faire, Django a un XMLField intégré.

Il y a aussi JSONField at Django snipplets.

3
muhuk

Cette question est ancienne, mais j'avais le même problème, s'est terminée ici et la réponse choisie ne pouvait plus résoudre mon problème.

Si vous souhaitez stocker des dictionnaires dans Django ou REST Api, soit pour être utilisés comme objets dans votre frontal, soit parce que vos données n'auront pas nécessairement la même structure, la solution que j'ai utilisée peut vous aider.

Lors de l'enregistrement des données dans votre API, utilisez la méthode json.dump () pour pouvoir les stocker dans un format json approprié, comme décrit dans cette question .

Si vous utilisez cette structure, vos données seront déjà au format json approprié pour être appelées en amont avec JSON.parse () dans votre appel ajax (ou autre).

3
Lucas Colzani

Django-Geo comprend un "DictionaryField" qui pourrait vous être utile:

http://code.google.com/p/Django-geo/source/browse/trunk/fields.py?r=13#49

En général, si vous n'avez pas besoin d'interroger les données, utilisez une approche dénormalisée pour éviter des requêtes supplémentaires. Les paramètres utilisateur sont un très bon exemple!

2
jb.

Réfléchissez-y et trouvez les points communs de chaque ensemble de données ... puis définissez votre modèle. Cela peut nécessiter ou non l'utilisation de sous-classes. Les clés étrangères représentant des points communs ne doivent pas être évitées, mais encouragées lorsqu'elles ont un sens.

Le remplissage de données aléatoires dans une table SQL n'est pas intelligent, à moins qu'il ne s'agisse vraiment de données non relationnelles. Si tel est le cas, définissez votre problème et nous pourrons peut-être vous aider.

2
Daniel Naab

Si vous utilisez Postgres, vous pouvez utiliser un champ hstore: https://docs.djangoproject.com/en/1.10/ref/contrib/postgres/fields/#hstorefield .

1
Scott Stafford

J'utilise un champ de texte et json.loads()/json.dumps()

models.py

import json
from Django.db import models

class Item(models.Model):
    data = models.TextField(blank=True, null=True, default='{}')

    def save(self, *args, **kwargs):
        ## load the current string and
        ## convert string to python dictionary
        data_dict = json.loads(self.data)

        ## do something with the dictionary
        for something in somethings:
            data_dict[something] = some_function(something)

        ## if it is empty, save it back to a '{}' string,
        ## if it is not empty, convert the dictionary back to a json string
        if not data_dict:
            self.data = '{}'
        else:
            self.data = json.dumps(data_dict)


        super(Item, self).save(*args, **kwargs)

0
Grady Woodruff