【问题标题】:How to create list field in django如何在 django 中创建列表字段
【发布时间】:2011-07-10 03:00:34
【问题描述】:

如何在 Django (Python) 中创建一个 ListField,如 Google App Engine (Python) 中的 ListProperty 属性?我的数据是这样的列表:3,4,5,6,7,8

我必须定义什么属性以及如何从中获取值?

【问题讨论】:

    标签: python django google-app-engine


    【解决方案1】:

    使用您可以使用的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]'
    

    你知道一个列表作为一个 unicode 字符串存储在数据库中,当它被拉出来时,它会通过ast.literal_eval() 运行。

    之前我从这篇关于 Custom Fields in 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)
    

    【讨论】:

    • 我是 App Engine 团队的一员,我知道它如何存储列表:作为一系列字段。例如,本机。
    • 尼克,我很想看看你的替代方案,在此之前 jathanism 的分离值字段方法非常适合我的 GAE Django-nonrel 项目。谢谢。
    • @NickJohnson “本地”是什么意思?当然,您并不是说它在关系数据库中已标准化。我希望 GAE 使用文档驱动的东西。
    • Django (1.5.2) 字段对象在 get_db_prep_value() 方法上请求“连接”参数。但根本不使用它?
    • 我现在明白你在说什么了。您对问题的阅读与我的不同。我来到这个问题是为了寻找“如何在 Django 中创建 ListField?”的答案。 (列表字段与 App Engine 中的类似)。
    【解决方案2】:

    尝试使用此处记录的CommaSeparatedIntegerFieldhttp://docs.djangoproject.com/en/1.2/ref/models/fields/#commaseparatedintegerfield

    【讨论】:

    • 你能告诉我们获取逗号分隔整数字段的单个值的代码吗??
    • 如果您的数据在列表中,例如[3,4,5],那么您将分配给CommaSeparatedIntegerField,如下所示:my_object.my_field = ",".join([str(x) for x in my_list])
    • App Engine 原生支持列表;以逗号分隔存储它们似乎是一个糟糕的主意。
    【解决方案3】:

    考虑django-jsonfield,优点是:

    • 保存和加载原生列表对象,无需转换
    • 经过良好测试且成熟的解决方案
    • 在您的项目中可能具有其他优势
    • 支持对数据库和正则表达式进行过滤(如果需要)

    还有:

    • 跨数据库支持
    • 支持 Python 2.7 到 Python 3.4 和 Django 1.4 到 1.8
    • 真的很容易:)

    【讨论】:

      【解决方案4】:

      虽然 jathanism 的回答很好,但在尝试使用 dumpdata 命令时出现以下错误:

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

      问题是value_to_string 方法中的self.get_db_prep_value 调用需要提供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
      

      【讨论】:

      • 你要做的就是改变方法如下: def get_db_prep_value(self, value,connection,prepared=False) 因为方法的签名被改变了
      • @OritK:怎么样?实现ListField 避免这个get_db_prep_value 问题的正确方法是什么?您的评论模棱两可:@jathanism 的实现没有定义 get_db_prep_value - 只是在没有 connection 参数可用的地方使用它。
      • 必须承认我不是 100% 确定我记得它.. 但根据我的评论:你应该改变 jathanism 的 ListField 实现。代替“def get_prep_value(self, value)”,输入“def get_db_prep_value(self, value,connection,prepared=False)”,它应该可以工作。
      • 我同意@OritK,这将是使@jathanism 的实现工作所需的最小更改。我认为该实现中存在一些不必要的更改,所以我删除了所有内容,但我认为创建ListField
      【解决方案5】:

      如果您使用的是 postgresql,django 支持带有 arrayfield 的 postgres。

      【讨论】:

        【解决方案6】:

        我这样做:

        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 函数的原因。

        【讨论】:

        • 它和你的separatedValuesField 类几乎一样,只是不是Field 类,也没有可配置的分隔符。
        • 功能上,是的,但我的意思是遵循 Django 框架中设置的约定。
        【解决方案7】:

        简单地说,您可以将列表存储为字符串,并且无论何时使用它,首先使用 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()
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-01-11
          • 1970-01-01
          • 1970-01-01
          • 2023-03-17
          相关资源
          最近更新 更多