38👍
Revisiting this with a ListField
type you can use. But it makes a few of assumptions, such as the fact that you’re not storing complex types in your list. For this reason I used ast.literal_eval()
to enforce that only simple, built-in types can be stored as members in a 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()
Taking it for a spin:
>>> 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]'
There you have it that a list is stored in the database as a unicode string, and when pulled back out it is run through ast.literal_eval()
.
Previously I suggested this solution from this blog post about Custom Fields in Django:
An alternative to the CommaSeparatedIntegerField, it allows you to
store any separated values. You can also optionally specify a token
parameter.
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)
6👍
Try using a CommaSeparatedIntegerField
which is documented here: http://docs.djangoproject.com/en/1.2/ref/models/fields/#commaseparatedintegerfield
- [Django]-What's the difference between Model.id and Model.pk in django?
- [Django]-What’s the difference between a project and an app in Django world?
- [Django]-Python 3 list(dictionary.keys()) raises error. What am I doing wrong?
6👍
Consider django-jsonfield, the advantages are:
- Save and load native list objects, no conversion needed
- Well tested and mature solution
- Could have other advantages within your project
- Supports filtering on the database and regex (if needed)
also:
- Cross database support
- Supports Python 2.7 to Python 3.4 and Django 1.4 to 1.8
- Really easy 🙂
- [Django]-Expire a view-cache in Django?
- [Django]-Django – Rollback save with transaction atomic
- [Django]-How to upload a file in Django?
2👍
While jathanism’s answer is great, I was getting the following error when trying to use the dumpdata
command:
Error: Unable to serialize database: get_db_prep_value() takes at least 3 arguments (2 given)
The issue is that self.get_db_prep_value
call in the value_to_string
method requires a connection
value to be provided (at least in Django 1.4.10, which is what I am using). In the end, I didn’t really see what was being gained by calling the value_to_string
method in the first place and removed it, along with the unnecessary __init__
method. This is what I ended up with:
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
- [Django]-Warning: Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'
- [Django]-Exception Value:failed to find libmagic. Check your installation in windows 7
- [Django]-Difference between django-redis-cache and django-redis for redis caching with Django?
- [Django]-Django: Generic detail view must be called with either an object pk or a slug
- [Django]-Dropdown in Django Model
- [Django]-Django auto_now and auto_now_add
1👍
I do this:
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')
But I suppose now it might be overkill. I needed quite a few of them, which is why I wrote the make_comma_field function.
- [Django]-Testing email sending in Django
- [Django]-Why don't my south migrations work?
- [Django]-Django admin: can I define fields order?
0👍
Simply, you can store the list as a string and whenever use it, use ast.literal_eval() to convert into list first from string. eg:
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
same way in views etc.
When saving, make oprations on list and lastly convert it into a string by:
model.list_field = str(list)
model.save()
- [Django]-Django setting environment variables in unittest tests
- [Django]-Django error: render_to_response() got an unexpected keyword argument 'context_instance'
- [Django]-Django Static files 404