Django ORM, как использовать values ​​() и при этом работать с полем выбора?

Я использую Джанго 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_number, created_at, pallet_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'))

Поскольку набор запросов вернет мне необработанные значения, как я могу использовать модель choicefield в PalletMovement, чтобы убедиться, что pallet_movement__location дает мне отображение AB-Delaware или CD-Delaware?

Имейте в виду, что main_model может быть любым в зависимости от того, что я храню в базе данных.

Предположительно, я могу хранить больше информации в базе данных, чтобы лучше фильтровать и представлять данные.


person Kim Stacks    schedule 05.08.2017    source источник
comment
Привет, @KimStacks, мне интересно, ты нашел какой-нибудь ответ полезным?   -  person John Moutafis    schedule 11.09.2017


Ответы (4)


Метод 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() попытается обновить данную пару ключ-значение, если она существует, иначе добавит ее в словарь.

Если использование вышеперечисленного будет в шаблонном представлении ваших данных, вы можете создать фильтр пользовательского шаблона вместо этого:

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

Посмотрите здесь: Django: Как получить доступ к отображаемому значению ChoiceField в шаблоне, учитывая фактическое значение и выбор?

person John Moutafis    schedule 09.08.2017

Вы бы использовали get_foo_display

В вашем шаблоне:

{{ obj.get_location_display }}

or

{{ obj.pallet_movement.get_location_display }}

[Редактировать:] Как указано в комментариях, это не будет работать при вызове values()

person The_Cthulhu_Kid    schedule 08.08.2017
comment
Обратите внимание, что вы не можете использовать get_foo_display, если вы используете values(). - person Alasdair; 08.08.2017

альтернативой созданию тега шаблона является:

{{form.choicefield.1}}

Это показывает значение начальных данных поля внешнего ключа вместо идентификатора.

person wololoooo    schedule 10.08.2017

Универсальное решение для любого main_model_name — это самоанализ Model _meta API Django. : 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' или с 'pallet' в display_columns. Даже то, что «_meta» начинается с подчеркивания, это задокументированный API.

person hynekcer    schedule 10.08.2017