【问题标题】:Django ORM, how to use values() and still work with choicefield?Django ORM,如何使用 values() 并且仍然可以使用choicefield?
【发布时间】:2018-01-13 03:06:27
【问题描述】:

我正在使用 django v1.10.2

我正在尝试创建动态报告,从而将字段和条件以及主要的 ORM 模型信息存储到数据库中。

我生成动态报告的代码是

class_object = class_for_name("app.models", main_model_name)

results = (class_object.objects.filter(**conditions_dict)
                               .values(*display_columns)
                               .order_by(*sort_columns)
                               [:50])

所以main_model_name 可以是任何东西。
这很好用,只是 main_model 的关联模型有时具有 choicefield

因此,main_model 的一份报告是Pallet
Pallet 有很多PalletMovement
我的显示栏目是:serial_numbercreated_atpallet_movement__location

前两列是属于Pallet 模型的字段。 最后一个来自PalletMovement

PalletMovement 模型看起来像这样:

class PalletMovement(models.Model):
    pallet = models.ForeignKey(Pallet, related_name='pallet_movements',
                               verbose_name=_('Pallet'))
    WAREHOUSE_CHOICES = (
        ('AB', 'AB-Delaware'),
        ('CD', 'CD-Delaware'),
    )
    location = models.CharField(choices=WAREHOUSE_CHOICES,
                                max_length=2,
                                default='AB',
                                verbose_name=_('Warehouse Location'))

由于查询集将返回原始值,我如何利用PalletMovement 模型中的choicefield 来确保pallet_movement__location 给我显示AB-DelawareCD-Delaware

请记住,main_model 可以是任何东西,具体取决于我在数据库中存储的内容。

大概,我可以在数据库中存储更多信息,以帮助我更好地过滤和呈现数据。

【问题讨论】:

  • 嘿,@KimStacks 我想知道,你觉得任何答案有帮助吗?

标签: python django django-queryset django-orm choicefield


【解决方案1】:

values() 方法返回一个键值对字典,表示您的字段名称和相应的值。

例如:

型号:

class MyModel(models.Model):
    name = models.CharField()
    surname = models.CharField()
    age = models.IntegerField()
    ...

查询:

result = MyModel.objects.filter(surname='moutafis').values('name', 'surname')

结果:

< Queryset [{'name': 'moutafis', 'surname': 'john'}] >

您现在可以像操作普通字典一样操作此结果:

if main_model_name is 'PalletMovement':
    # Make life easier
    choices = dict(PalletMovement.WAREHOUSE_CHOICES)

    for item in result:
        item.update({ 
            pallet_movement__location: verbal_choice.get(
                pallet_movement__location, pallet_movement__location)
        })

你甚至可以把它变成一个函数,以获得更好的重用性:

def verbalize_choices(choices_dict, queryset, search_key):
    result = queryset        

    for item in result:
        item.update({ search_key: choices_dict.get(search_key, search_key) })

    return result

verbal_result = verbalize_choices(
                    dict(PalletMovement.WAREHOUSE_CHOICES),
                    result,
                    'pallet_movement__location'
                )

我建议使用update()get() 方法,因为它们可以帮助您避免潜在的错误,例如:

  • search_key 不存在于choice_dict 中,那么get() 将返回search_key 的值
  • update() 将尝试更新给定的键值对(如果存在),否则会将其添加到字典中。

如果上面的用法将在您的数据的模板表示中,您可以创建一个custom template filter 代替:

@register.filter(name='verbalize_choice')
def choice_to_verbal(choice):
    return dict(PalletMovement.WAREHOUSE_CHOICES)[choice]

多看这里:Django: How to access the display value of a ChoiceField in template given the actual value and the choices?

【讨论】:

    【解决方案2】:

    你会使用get_foo_display

    在您的模板中:

    {{ obj.get_location_display }}
    

    {{ obj.pallet_movement.get_location_display }}
    

    [编辑:] 正如 cmets 中所指出的,这在调用 values() 时不起作用

    【讨论】:

    • 请注意,如果您使用values(),则不能使用get_foo_display
    【解决方案3】:

    创建模板标签的另一种方法是:

    {{form.choicefield.1}}
    

    这里显示的是外键字段的初始数据的值,而不是id。

    【讨论】:

      【解决方案4】:

      任何main_model_name 的通用解决方案是 Django Model _meta API 内省:class_object._meta.get_field(field_name).choices
      那就是:

      choice_dicts = {}
      for field_name in display_columns:
          choice_dicts[field_name] = {
              k: v for k, v in class_object._meta.get_field(field_name).choices
          }
      out = []
      for row in results:
          out.append({name: choice_dicts[name].get(value, value)
                      for name, value in row.items()
                      })
      

      剩下的是一个琐碎的例子,大​​部分是从问题中复制的代码

      >>> pallet = app.models.Pallet.objects.create()
      >>> palletm = app.models.PalletMovement.objects.create(pallet=pallet, location='AB')
      >>>
      >>> main_model_name = 'PalletMovement'
      >>> conditions_dict = {}
      >>> display_columns = ['pallet_id', 'location']
      >>> sort_columns = []
      >>>
      >>> class_object = class_for_name("app.models", main_model_name)
      >>> results = (class_object.objects.filter(**conditions_dict)
      ...                                .values(*display_columns)
      ...                                .order_by(*sort_columns)
      ...            )[:50]
      >>>
      >>> # *** INSERT HERE ALL CODE THAT WAS ABOVE ***
      >>>
      >>> print(out)
      [{'location': 'AB-Delaware', 'pallet_id': 1}]
      

      它同样适用于“pallet_id”或display_columns 中的“pallet”。即使“_meta”以下划线开头,它也是一个文档化的 API。

      【讨论】:

        猜你喜欢
        • 2011-10-22
        • 2018-06-13
        • 1970-01-01
        • 1970-01-01
        • 2014-09-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多