Jinja - Как передать динамический диапазон выражению?

У меня есть файл csv, который я читаю как dict, используя Ansible read_csv

id,name,quantity,type
1,apple,10,fruit
2,orange,20,fruit
3,carrot,5,veg
4,beetroot,2,veg
5,onion,3,veg
6,tomato,4,both
7,pear,4,fruit
8,banana,6,fruit
9,persimon,4,fruit
10,guava,4,fruit
11,pepper,4,veg
12,potato,5,veg
13,cherry,5,fruit

Эквивалентный дикт становится

"{'1': {'id': '1', 'name': 'apple', 'quantity': '10', 'type': 'fruit'}, '2': {'id': '2', 'name': 'orange', 'quantity': '20', 'type': 'fruit'}, '3': {'id': '3', 'name': 'carrot', 'quantity': '5', 'type': 'veg'}, '4': {'id': '4', 'name': 'beetroot', 'quantity': '2', 'type': 'veg'}, '5': {'id': '5', 'name': 'onion', 'quantity': '3', 'type': 'veg'}, '6': {'id': '6', 'name': 'tomato', 'quantity': '4', 'type': 'both'}, '7': {'id': '7', 'name': 'pear', 'quantity': '4', 'type': 'fruit'}, '8': {'id': '8', 'name': 'banana', 'quantity': '6', 'type': 'fruit'}, '9': {'id': '9', 'name': 'persimon', 'quantity': '4', 'type': 'fruit'}, '10': {'id': '10', 'name': 'guava', 'quantity': '4', 'type': 'fruit'}, '11': {'id': '11', 'name': 'pepper', 'quantity': '4', 'type': 'veg'}, '12': {'id': '12', 'name': 'potato', 'quantity': '5', 'type': 'veg'}, '13': {'id': '13', 'name': 'cherry', 'quantity': '5', 'type': 'fruit'}}"

Моя логика заключалась в том, чтобы разделить список на партии по 2 имени каждого типа за раз. Итак, результат, который я искал, это ["carrot", "beetroot"], ["onion", "pepper"] и так далее.

Приведенная ниже логика отлично работает, когда я жестко запрограммирую диапазон [0:2] в выражении jinja.

{% set my_fruit_list = [] %}
{%- for item in (fruits_dict.dict| dict2items | selectattr("value.type", "match", "^veg$"))[0:2]  -%}
{{ my_fruit_list.append(item.value.name) }}
{%- endfor -%}
my_list=["{{ my_fruit_list|join('", "') }}"]

Но когда я пытаюсь изменить его на динамическую переменную, это не работает. я пробовал ниже

{% set input_range = "[0:2]" %}
{%- for item in (fruits_dict.dict| dict2items | selectattr("value.type", "match", "^veg$"))input_range  -%}

Можем ли мы каким-либо образом передать «input_range» в качестве динамического параметра в выражение?

Также есть ли лучший способ получить selectattr без преобразования csv в dict и dict2items?

Введение в Ansible Roles
Введение в Ansible Roles
Ansible - это отличный инструмент управления конфигурацией, который можно использовать для автоматизации настройки или развертывания на большом...
2
1
58
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Given the file

shell> cat /tmp/fruits.csv 
id,name,quantity,type
1,apple,10,fruit
2,orange,20,fruit
3,carrot,5,veg
4,beetroot,2,veg
5,onion,3,veg
6,tomato,4,both
7,pear,4,fruit
8,banana,6,fruit
9,persimon,4,fruit
10,guava,4,fruit
11,pepper,4,veg
12,potato,5,veg
13,cherry,5,fruit

Read the file and create a dictionary by the key id

    - read_csv:
        path: /tmp/fruits.csv
        key: id
      register: fruits

gives

  fruits.dict:
    '1': {id: '1', name: apple, quantity: '10', type: fruit}
    '10': {id: '10', name: guava, quantity: '4', type: fruit}
    '11': {id: '11', name: pepper, quantity: '4', type: veg}
    '12': {id: '12', name: potato, quantity: '5', type: veg}
    '13': {id: '13', name: cherry, quantity: '5', type: fruit}
    '2': {id: '2', name: orange, quantity: '20', type: fruit}
    '3': {id: '3', name: carrot, quantity: '5', type: veg}
    '4': {id: '4', name: beetroot, quantity: '2', type: veg}
    '5': {id: '5', name: onion, quantity: '3', type: veg}
    '6': {id: '6', name: tomato, quantity: '4', type: both}
    '7': {id: '7', name: pear, quantity: '4', type: fruit}
    '8': {id: '8', name: banana, quantity: '6', type: fruit}
    '9': {id: '9', name: persimon, quantity: '4', type: fruit}

  1. Получите значения и сгруппируйте их по типу
  by_type: "{{ fruits.dict.values()|groupby('type') }}"

дает

  by_type:
    - - both
      - - {id: '6', name: tomato, quantity: '4', type: both}
    - - fruit
      - - {id: '1', name: apple, quantity: '10', type: fruit}
        - {id: '2', name: orange, quantity: '20', type: fruit}
        - {id: '7', name: pear, quantity: '4', type: fruit}
        - {id: '8', name: banana, quantity: '6', type: fruit}
        - {id: '9', name: persimon, quantity: '4', type: fruit}
        - {id: '10', name: guava, quantity: '4', type: fruit}
        - {id: '13', name: cherry, quantity: '5', type: fruit}
    - - veg
      - - {id: '3', name: carrot, quantity: '5', type: veg}
        - {id: '4', name: beetroot, quantity: '2', type: veg}
        - {id: '5', name: onion, quantity: '3', type: veg}
        - {id: '11', name: pepper, quantity: '4', type: veg}
        - {id: '12', name: potato, quantity: '5', type: veg}
  1. Создать словарь
  my_dict: "{{ dict(by_type|map('first')|list|
                    zip(by_type|map('last')|
                                map('map', attribute='name')|list)) }}"

дает

  my_dict:
    both: [tomato]
    fruit: [apple, orange, pear, banana, persimon, guava, cherry]
    veg: [carrot, beetroot, onion, pepper, potato]
  1. Создавайте партии товаров. Где type и input_range являются параметрами
    my_list: |
      {% for batch in my_dict[type]|batch(input_range|int) %}
        - {{ batch }}
      {% endfor %}

Например, петля ниже

    - debug:
        msg: "{{ my_list }}"
      loop: "{{ my_dict.keys()|list }}"
      vars:
        type: "{{ item }}"

за input_range=2 дает

TASK [debug] **************************************************************
ok: [localhost] => (item=both) => 
  msg: |2-
      - ['tomato']
ok: [localhost] => (item=fruit) => 
  msg: |2-
      - ['apple', 'orange']
      - ['pear', 'banana']
      - ['persimon', 'guava']
      - ['cherry']
ok: [localhost] => (item=veg) => 
  msg: |2-
      - ['carrot', 'beetroot']
      - ['onion', 'pepper']
      - ['potato']

за input_range=3 дает

TASK [debug] **************************************************************
ok: [localhost] => (item=both) => 
  msg: |2-
      - ['tomato']
ok: [localhost] => (item=fruit) => 
  msg: |2-
      - ['apple', 'orange', 'pear']
      - ['banana', 'persimon', 'guava']
      - ['cherry']
ok: [localhost] => (item=veg) => 
  msg: |2-
      - ['carrot', 'beetroot', 'onion']
      - ['pepper', 'potato']
  1. Создавайте конкретные списки
    - set_fact:
        my_list_veg: "{{ my_list }}"
      vars:
        type: veg
        input_range: 3
    - debug:
        var: my_list_veg

дает

  my_list_veg:
      - ['carrot', 'beetroot', 'onion']
      - ['pepper', 'potato']

  • Example of a complete playbook for testing
shell> cat pb.yml
- hosts: localhost
  vars:
    input_range: 2
    by_type: "{{ fruits.dict.values()|groupby('type') }}"
    my_dict: "{{ dict(by_type|map('first')|list|
                      zip(by_type|map('last')|
                                  map('map', attribute='name')|list)) }}"
    my_list: |
      {% for batch in my_dict[type]|batch(input_range|int) %}
        - {{ batch }}
      {% endfor %}
      
  tasks:
    - read_csv:
        path: /tmp/fruits.csv
        key: id
      register: fruits
    - debug:
        var: fruits.dict|to_yaml
    - debug:
        var: by_type|to_yaml
    - debug:
        var: my_dict|to_yaml

    - debug:
        msg: "{{ my_list }}"
      loop: "{{ my_dict.keys()|list }}"
      vars:
        type: "{{ item }}"

    - set_fact:
        my_list_veg: "{{ my_list }}"
      vars:
        type: veg
        input_range: 3
    - debug:
        var: my_list_veg
  • You can override input_range from the command line, e.g.
shell> ansible-playbook pb.yml -e input_range=3
  • Q: "I needed a combination of 'name'-'type'-'id' as my 'my_list'"

A: Create a dictionary with all attributes

  my_dict: "{{ dict(by_type) }}"

gives

  my_dict:
    both:
    - {id: '6', name: tomato, quantity: '4', type: both}
    fruit:
    - {id: '1', name: apple, quantity: '10', type: fruit}
    - {id: '2', name: orange, quantity: '20', type: fruit}
    - {id: '7', name: pear, quantity: '4', type: fruit}
    - {id: '8', name: banana, quantity: '6', type: fruit}
    - {id: '9', name: persimon, quantity: '4', type: fruit}
    - {id: '10', name: guava, quantity: '4', type: fruit}
    - {id: '13', name: cherry, quantity: '5', type: fruit}
    veg:
    - {id: '3', name: carrot, quantity: '5', type: veg}
    - {id: '4', name: beetroot, quantity: '2', type: veg}
    - {id: '5', name: onion, quantity: '3', type: veg}
    - {id: '11', name: pepper, quantity: '4', type: veg}
    - {id: '12', name: potato, quantity: '5', type: veg}

Then, select the attributes you want. For example, select the attributes id and name and create the template

    my_list: |
      {% for batch in my_dict[type]|batch(input_range|int) %}
      {% for i in batch %}
      - id: {{ i.id }}
        name: {{ i.name }}
      {% endfor %}
      {% endfor %}

Then create specific lists, for example

    - set_fact:
        my_list_veg: "{{ my_list|from_yaml }}"
      vars:
        type: veg
        input_range: 3
    - debug:
        var: my_list_veg

gives

  my_list_veg:
    - {id: 3, name: carrot}
    - {id: 4, name: beetroot}
    - {id: 5, name: onion}
    - {id: 11, name: pepper}
    - {id: 12, name: potato}

как всегда очень хорошая перспектива и плюс

diaryfolio 23.11.2022 09:54

я планирую переключить свою логику на то, что вы описали, так как это превосходно. Но я застрял с `my_dict: "{{ dict(by_type|map('first')|list| zip(by_type|map('last')| map('map', attribute='name')|list )) }}"`, так как мне нужна была комбинация 'name'-'type'-'id' в качестве моего "my_list". Есть ли способ, которым мы можем иметь несколько атрибутов?

diaryfolio 23.11.2022 14:37

Я добавил пример. Откройте новый вопрос и сделайте его минимально воспроизводимым примером, если у вас есть проблемы.

Vladimir Botka 23.11.2022 20:39
Ответ принят как подходящий

В двух словах:

{% set my_fruit_list = [] %}
{% set fruit_start = 0 %}
{% set fruit_end = 1 %}
{%- for item in (fruits_dict.dict| dict2items | selectattr("value.type", "match", "^veg$"))[fruit_start:fruit_end]  -%}
{{ my_fruit_list.append(item.value.name) }}
{%- endfor -%}
my_list=["{{ my_fruit_list|join('", "') }}"]

ух ты. это было так просто. Спасибо тебе, друг. проголосовал и сделал это как ответ

diaryfolio 23.11.2022 09:53

Другие вопросы по теме