Как создать поле списка в Django - PullRequest
27 голосов
/ 07 марта 2011

Как мне создать ListField в Django (Python), например, свойство ListProperty в Google App Engine (Python)? Мои данные в виде списка: 3,4,5,6,7,8.

Какое свойство я должен определить и как мне получить значения из него?

Ответы [ 7 ]

35 голосов
/ 13 сентября 2011

Возвращаясь к этому типу ListField, который вы можете использовать.Но он делает несколько предположений, например, тот факт, что вы не храните сложные типы в вашем списке.По этой причине я использовал ast.literal_eval() для обеспечения того, чтобы только простые встроенные типы могли храниться в качестве элементов в 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()

Принимая это за вращение:

>>> 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]'

Там у вас есть то, что список хранится в базе данных в виде строки в юникоде, и когда он извлекается, он запускается через ast.literal_eval().

Ранее я предлагал это решениеиз этого поста о пользовательских полях в Django :

Альтернатива CommaSeparatedIntegerField, она позволяет хранить любые отдельные значения.Вы также можете дополнительно указать параметр токена.

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)
5 голосов
/ 07 марта 2011

Попробуйте использовать CommaSeparatedIntegerField, что описано здесь: http://docs.djangoproject.com/en/1.2/ref/models/fields/#commaseparatedintegerfield

4 голосов
/ 29 октября 2015

Рассмотрим django-jsonfield , преимущества:

  • Сохранение и загрузка объектов собственного списка, преобразование не требуется
  • Хорошо протестированное и зрелое решение
  • Может иметь другие преимущества в вашем проекте
  • Поддерживает фильтрацию по базе данных и регулярное выражение (при необходимости)

также:

  • Перекрестная база данныхподдержка
  • Поддерживает Python 2.7 до Python 3.4 и Django 1.4 до 1.8
  • Действительно просто:)
2 голосов
/ 26 ноября 2015

Если вы используете postgresql, django поддерживает postgres с arrayfield .

2 голосов
/ 04 февраля 2014

Хотя ответ jathanism велик, при попытке использования команды dumpdata я получил следующую ошибку:

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

Проблема в том, что для вызова self.get_db_prep_value в методе value_to_string необходимо указать значение connection (по крайней мере, в Django 1.4.10 , что я и использую). В конце концов, я действительно не увидел, что получалось, если сначала вызвал метод value_to_string, и удалил его вместе с ненужным методом __init__. Вот чем я закончил:

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
1 голос
/ 13 сентября 2011

Я делаю это:

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')

Но я полагаю, теперь это может быть излишним. Мне понадобилось довольно много из них, поэтому я написал функцию make_comma_field.

0 голосов
/ 07 сентября 2016

Просто вы можете сохранить список в виде строки и всякий раз, когда его используете, используйте ast.literal_eval (), чтобы сначала преобразовать в список из строки. например:

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

таким же образом в представлениях и т. Д. При сохранении сделайте опцию в списке и, наконец, преобразуйте его в строку следующим образом:

model.list_field = str(list)
model.save()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...