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?
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)
Essayez d'utiliser un CommaSeparatedIntegerField
qui est documenté ici: http://docs.djangoproject.com/en/1.2/ref/models/fields/#commaseparatedintegerfield
Considérez Django-jsonfield , les avantages sont:
aussi:
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
Si vous utilisez postgresql, Django prend en charge postgres avec arrayfield .
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.
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()