web-dev-qa-db-fra.com

Comment créer un champ de liste dans django

Comment créer un ListField dans Django (Python) comme la propriété ListProperty dans Google App Engine (Python)? Mes données sont une liste comme celle-ci: 3,4,5,6,7,8.

Quelle propriété dois-je définir et comment en extraire des valeurs?

28
Madhur

Revisiter cela avec un type ListField que vous pouvez utiliser. Mais cela fait quelques hypothèses, telles que le fait que vous ne stockez pas de types complexes dans votre liste. Pour cette raison, j'ai utilisé ast.literal_eval() pour imposer que seuls les types simples intégrés peuvent être stockés en tant que membres dans un ListField:

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 Dummy(models.Model):
    mylist = ListField()

Prenant pour un tour:

>>> from foo.models import Dummy, ListField
>>> d = Dummy()
>>> d.mylist
[]
>>> d.mylist = [3,4,5,6,7,8]
>>> d.mylist
[3, 4, 5, 6, 7, 8]
>>> f = ListField()
>>> f.get_prep_value(d.numbers)
u'[3, 4, 5, 6, 7, 8]'

Voilà, une liste est stockée dans la base de données sous forme de chaîne unicode, et lorsqu'elle est retirée, elle est exécutée par ast.literal_eval() .

Auparavant, j'ai suggéré cette solution à partir de ce billet de blog sur Champs personnalisés dans Django :

Une alternative à CommaSeparatedIntegerField, il vous permet de stocker toutes les valeurs séparées. Vous pouvez également éventuellement spécifier un paramètre de jeton.

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)
35
jathanism

Essayez d'utiliser un CommaSeparatedIntegerField qui est documenté ici: http://docs.djangoproject.com/en/1.2/ref/models/fields/#commaseparatedintegerfield

5
Isaac C.

Considérez Django-jsonfield , les avantages sont:

  • Enregistrez et chargez les objets de la liste native, aucune conversion nécessaire
  • Solution bien testée et mature
  • Pourrait avoir d'autres avantages dans votre projet
  • Prend en charge le filtrage sur la base de données et l'expression régulière (si nécessaire)

aussi:

  • Prise en charge de plusieurs bases de données
  • Prend en charge Python 2,7 à Python 3,4 et Django 1,4 à 1,8)
  • Vraiment facile :)
4
oden

Bien que la réponse de jathanism soit excellente, j'obtenais l'erreur suivante lorsque j'essayais d'utiliser la commande dumpdata:

Error: Unable to serialize database: get_db_prep_value() takes at least 3 arguments (2 given)

Le problème est que l'appel self.get_db_prep_value Dans la méthode value_to_string Nécessite une valeur connection à fournir (au moins dans Django 1.4.1 , qui est ce que j'utilise). En fin de compte, je n'ai pas vraiment vu ce qui était gagné en appelant la méthode value_to_string En premier lieu et je l'ai supprimée, ainsi que la méthode __init__ Inutile. Voici ce que j'ai fini avec:

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

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

        if isinstance(value, list):
            return value

        converted = ast.literal_eval(value)
        if not isinstance(converted, list):
            raise ValueError('Value "%s" not a list' % converted)

        return converted
2
alukach

Si vous utilisez postgresql, Django prend en charge postgres avec arrayfield .

2
Leonard2

Je fais ça:

def get_comma_field(self, field):
    data = getattr(self, field)
    if data:
        return data.split(',')
    return []


def set_comma_field(self, val, field):
    if isinstance(val, types.StringTypes):
        setattr(self, field, val)
    else:
        setattr(self, field, ','.join(val))


def make_comma_field(field):
    def getter(self):
        return get_comma_field(self, field)

    def setter(self, val):
        return set_comma_field(self, val, field)

    return property(getter, setter)

class MyModel(models.Model):
    _myfield = models.CharField(max_length=31)
    myfield = make_comma_field('_myfield')

Mais je suppose que maintenant c'est peut-être exagéré. J'en avais besoin de plusieurs, c'est pourquoi j'ai écrit la fonction make_comma_field.

1
Aaron McMillin

Simplement, vous pouvez stocker la liste sous forme de chaîne et chaque fois que vous l'utilisez, utilisez ast.literal_eval () pour convertir en liste d'abord à partir de la chaîne. par exemple:

import ast

class MyModel(models.Model):
    field_1 = models.any kind of field()
    list_field = models.CharField(max_length=255)

    def get_list(self):
        list = ast.literal_eval(self.list_field)
        return list

de la même manière dans les vues, etc. Lors de l'enregistrement, effectuez des opérations sur la liste et convertissez-la enfin en chaîne par:

model.list_field = str(list)
model.save()
0
sprksh