【问题标题】:Django: values_list() multiple fields concatenatedDjango:values_list() 连接多个字段
【发布时间】:2012-07-10 16:23:28
【问题描述】:

我有一个Person 模型,我正在使用 django 表单来编辑另一个对象,该对象的外键为Person。 person 模型有 first_namelast_name 字段。我想运行一个方法来过滤外部引用下拉框的结果。

我正在尝试使用values_list() 来覆盖表单字段选项(选择属性),如下所示:

data.form.fields['person'].choices = GetPersons().values_list('id', 'first_name')

GetPersons() 只是像过滤 Person 类一样

return Person.objects.filter(id__gt=1000)`

例如,所以我只邀请我想出现的人。如何使用values_list() 来返回first_namelast_name 的串联,而不必返回字典并手动拆分所有内容?

【问题讨论】:

    标签: django concatenation


    【解决方案1】:

    我想给你 2 条建议:

    • 第一个是将数据库中的字段与extra 连接起来。对我来说是一个肮脏的解决方案,但可以运行。

    示例:

    persons =  GetPersons().extra(select={'full_name': "concatenate( first, last) "} )
    choices = persons.values_list('id', 'full_name')
    

    和...

    • 第二个使用列表理解:

    示例:

    choices = [ ( p.id, '{0} {1}'.format( p.first, p.last ),) for p in GetPersons() ]
    

    2018 年编辑

    Concat 现在可用作数据库函数:

    >>> from django.db.models import CharField, Value as V
    >>> from django.db.models.functions import Concat
    >>> persons = GetPersons().annotate(
    ...     full_name=Concat(
    ...         'last', V(', '), 'first', V('.'),
    ...         output_field=CharField()
    ...     )
    ... )
    

    【讨论】:

      【解决方案2】:

      听起来annotate() function 在 Django 1.8 中变得更加灵活。您可以将两个字段与 Concat expression 组合,然后使用该表达式注释查询集。

      # Tested with Django 1.9.2
      import sys
      
      import django
      from django.apps import apps
      from django.apps.config import AppConfig
      from django.conf import settings
      from django.db import connections, models, DEFAULT_DB_ALIAS
      from django.db.models.base import ModelBase
      from django.db.models.functions import Concat, Value
      
      NAME = 'udjango'
      
      
      def main():
          setup()
      
          class Person(models.Model):
              first_name = models.CharField(max_length=30)
              last_name = models.CharField(max_length=30)
      
          syncdb(Person)
      
          Person.objects.create(first_name='Jimmy', last_name='Jones')
          Person.objects.create(first_name='Bob', last_name='Brown')
      
          print(Person.objects.annotate(
              full_name=Concat('first_name',
                               Value(' '),
                               'last_name')).values_list('id', 'full_name'))
          # >>> [(1, u'Jimmy Jones'), (2, u'Bob Brown')]
      
      
      def setup():
          DB_FILE = NAME + '.db'
          with open(DB_FILE, 'w'):
              pass  # wipe the database
          settings.configure(
              DEBUG=True,
              DATABASES={
                  DEFAULT_DB_ALIAS: {
                      'ENGINE': 'django.db.backends.sqlite3',
                      'NAME': DB_FILE}},
              LOGGING={'version': 1,
                       'disable_existing_loggers': False,
                       'formatters': {
                          'debug': {
                              'format': '%(asctime)s[%(levelname)s]'
                                        '%(name)s.%(funcName)s(): %(message)s',
                              'datefmt': '%Y-%m-%d %H:%M:%S'}},
                       'handlers': {
                          'console': {
                              'level': 'DEBUG',
                              'class': 'logging.StreamHandler',
                              'formatter': 'debug'}},
                       'root': {
                          'handlers': ['console'],
                          'level': 'WARN'},
                       'loggers': {
                          "django.db": {"level": "WARN"}}})
          app_config = AppConfig(NAME, sys.modules['__main__'])
          apps.populate([app_config])
          django.setup()
          original_new_func = ModelBase.__new__
      
          @staticmethod
          def patched_new(cls, name, bases, attrs):
              if 'Meta' not in attrs:
                  class Meta:
                      app_label = NAME
                  attrs['Meta'] = Meta
              return original_new_func(cls, name, bases, attrs)
          ModelBase.__new__ = patched_new
      
      
      def syncdb(model):
          """ Standard syncdb expects models to be in reliable locations.
      
          Based on https://github.com/django/django/blob/1.9.3
          /django/core/management/commands/migrate.py#L285
          """
          connection = connections[DEFAULT_DB_ALIAS]
          with connection.schema_editor() as editor:
              editor.create_model(model)
      
      main()
      

      【讨论】:

      • 谢谢你,很好的解决方案!我认为这是一种比接受的答案更普遍支持的方法?
      • 我假设所有 Django 后端都支持这一点,@Gesias。例如,我认为接受的答案不适用于 Sqlite3,因为它与 || 运算符连接。公平地说,我还没有尝试过。
      • 很好的答案!您知道如何在此过程中转换类型吗?例如,如果我想将 id 转换为字符串,然后将其与名称连接怎么办?
      • 这可能取决于数据库@J__,但我只是尝试在此处的示例中将'first_name' 替换为'id',并且效果很好。
      • 是的,我也成功了,文档对此有点困惑。谢谢!
      【解决方案3】:

      Per:Is it possible to reference a property using Django's QuerySet.values_list?,在不适用时避免使用 values_list,而是使用理解。

      models.py:
      class Person(models.Model):
          first_name = models.CharField(max_length=32)
          last_name = models.CharField(max_length=64)
          def getPrintName(self):
              return self.last_name + ", " + self.first_name
      
      views.py:
      data.form.fields['person'].choices = [(person.id, person.getPrintName()) for person in GetPersons()]
      

      【讨论】:

        猜你喜欢
        • 2013-10-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-26
        • 2014-06-22
        • 1970-01-01
        相关资源
        最近更新 更多