Pregunta

Tengo una plantilla en la que coloqué, digamos 5 formularios, pero todos deshabilitados para ser publicados, excepto el primero. El siguiente formulario solo se puede llenar si hago clic en un botón que lo habilite primero.

Estoy buscando una manera de implementar una variable de Templatetag tipo Django.

Básicamente, lo que necesito hacer es algo así:

for form_data in step.hashes:
    # get and fill the current form with data in form_data
    if not forloop.last:
        # click the button that enables the next form
# submit all filled forms
¿Fue útil?

Solución

Si entiendo su pregunta correctamente, ¿desea una prueba simple para si está al principio o al final de la lista?

Si ese es el caso, esto lo haría:

for item in list:
    if item != list[-1]:
        #Do stuff

Para el primer elemento de la lista, reemplazaría "-1" con 0.

Otros consejos

No conozco nada incorporado, pero puede escribir fácilmente un generador para brindarle la información requerida:

def firstlast(seq):
    seq = iter(seq)
    el = prev = next(seq)
    is_first = True
    for el in seq:
        yield prev, is_first, False
        is_first = False
        prev = el
    yield el, is_first, True


>>> list(firstlast(range(4)))
[(0, True, False), (1, False, False), (2, False, False), (3, False, True)]
>>> list(firstlast(range(0)))
[]
>>> list(firstlast(range(1)))
[(0, True, True)]
>>> list(firstlast(range(2)))
[(0, True, False), (1, False, True)]
>>> for count, is_first, is_last in firstlast(range(3)):
    print(count, "first!" if is_first else "", "last!" if is_last else "")


0 first! 
1  
2  last!

Podrías usar enumerate y compare el contador con la longitud de la lista:

for i, form_data in enumerate(step.hashes):
    if i < len(step.hashes):
        whatever()
for form_data in step.hashes[:-1]:
    # get and fill the current form with data in form_data
for form_data in step.hashes[-1:]:
    # get and fill the current form with data in form_data
    # click the button that enables the next form
# submit all filled forms

No me gusta la repetición de get and fill the current form with data in form_data? Definir una función.

Generador con búfer.

def first_last( iterable ):
    i= iter(iterable)
    f= next(i)
    yield f, "first"
    n= next(i)
    for another in i:
        yield n, None
        n= another
    yield n, "last"

for item, state in first_list( iterable ):
    # state is "first", None or "last". 

Zapante de dos secuencias

flags = ["first"] + (len(iterable)-2)*[None] + ["last"]
for item, state in zip( iterable, flags ):
    # state is "first", None or "last".

Creo que quiere tener un envoltorio alrededor del iterador que proporciona las primeras / últimas consultas, también el parámetro podría ser un iterador, por lo que todo tipo de len () fallaría

Aquí es lo que surgí hasta ahora, el truco es usar un doble iterador, uno que mira hacia adelante un paso del primero:

class FirstLastIter(object):

    def __init__(self, seq):
        self._seq_iter = iter(seq)
        self._seq_iter_next = iter(seq)
        self._idx = -1
        self._last = None
        self.next_next()

    @property
    def first(self):
        return self._idx == 0

    @property
    def last(self):
        return self._last == True

    def __iter__(self):
        return self

    def next_next(self):
        try:
            self._seq_iter_next.next()
        except StopIteration:
            self._last = True

    def next(self):
        val = self._seq_iter.next()
        self._idx += 1
        self.next_next()
        return val

for x in FirstLastIter([]):
    print x

iterator = FirstLastIter([1])
for x in iterator:
    print x,iterator.first,iterator.last

iterator = FirstLastIter([1,2,3])
for x in iterator:
    print x,iterator.first,iterator.last

devoluciones:

1 True True
1 True False
2 False False
3 False True

Antes de que alguien aguante sus antorchas o encienda las camtadas, no soy un experto en lo que es Pitónico, que dijo, me parece que si first y/o last es buscado de una lista, en el desde entonces if first o if last Dentro de un bucle, parece esperado super la clase y el complemento de la funcionalidad que se desea ... tal vez, entonces lo que sigue es la versión totalmente prealpha E-1^11% de código que puede causar estragos si se mira el martes de la manera correcta. .

import sys
## Prevent `.pyc` (Python byte code) files from being generated
sys.dont_write_bytecode = True

from collections import OrderedDict


class MetaList(list):
    """
    Generates list of metadata dictionaries for list types

    ## Useful resources

    - [C Source for list](https://github.com/python/cpython/blob/master/Objects/listobject.c)
    - [Supering `list` and `collections.MutableSequence`](https://stackoverflow.com/a/38446773/2632107)
    """

    # List supering methods; maybe buggy but seem to work so far...
    def __init__(self, iterable = [], **kwargs):
        """
        > Could not find what built in `list()` calls the initialized lists during init... might just be `self`...
        > If feeling cleverer check the C source. For now this class will keep a copy

        ## License [GNU_GPL-2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)

            Generates list of metadata dictionaries for lists types
            Copyright (C) 2019  S0AndS0

            This program is free software; you can redistribute it and/or
            modify it under the terms of the GNU General Public License
            as published by the Free Software Foundation; version 2
            of the License.

            This program is distributed in the hope that it will be useful,
            but WITHOUT ANY WARRANTY; without even the implied warranty of
            MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
            GNU General Public License for more details.

            You should have received a copy of the GNU General Public License
            along with this program; if not, write to the Free Software
            Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
        """
        self.metadata = []
        for index, value in enumerate(iterable):
            if isinstance(value, list):
                sub_kwargs = {}
                sub_kwargs.update(kwargs)
                sub_kwargs['address'] = kwargs.get('address', [index])
                sub_list = MetaList(iterable = value, **sub_kwargs)
                self.append(sub_list, **kwargs)
            else:
                self.append(value, **kwargs)

        # Note; supering order matters when using built in methods during init
        super(MetaList, self).__init__(iterable)

    def __add__(self, other):
        """
        Called when adding one list to another, eg `MetaList([1,2,3]) + [9,8,7]`

        - Returns copy of list plus `other`, sorta like `self.extend` but without mutation

        ## Example input

            test_list = MetaList([1 ,2, 3])
            longer_list = test_list + [4, 5, 6]

        ## Example output

            print("#\ttest_list -> {0}".format(test_list))
            #   test_list -> [1, 2, 3]
            print("#\tlonger_list -> {0}".format(longer_list))
            #   longer_list -> [1, 2, 3, 4, 5, 6]
        """
        super(MetaList, self).__add__(other)
        output = MetaList(self)
        output.extend(other)
        return output

    def __setitem__(self, index, item, **kwargs):
        """
        Called when setting values by index, eg `listing[0] = 'value'`, this updates `self` and `self.metadata`
        """
        super(MetaList, self).__setitem__(index, item)

        address = kwargs.get('address', []) + [index]
        value = item

        dictionary = self.__return_dictionary(
            address = address,
            index = index,
            value = value)

        self.metadata[index] = dictionary
        self.__refresh_first()
        self.__refresh_last()
        self.__refresh_indexes(start = index)

    def append(self, item, **kwargs):
        """
        Appends to `self.metadata` an `OrderedDict` with the following keys

        - `address`: `[0]` or `[0, 1, 5]` list of indexes mapping to `value`
        - `index`: `0` or `42` integer of index within current listing
        - `value`: `string`, `['list']`, `{'dict': 'val'}`, etc; not enabled by default
        - `first`: `True`/`False` boolean; item is first in current listing
        - `last`: `True`/`False` boolean; item is last in current listing
        """
        super(MetaList, self).append(item)

        # Update last status of previously last item within `self.metadata`
        if self.metadata:
            self.metadata[-1]['last'] = False

        index = len(self.metadata)
        address = kwargs.get('address', []) + [index]

        value = item

        dictionary = self.__return_dictionary(
            address = address,
            index = index,
            value = value)

        dictionary['first'] = False
        dictionary['last'] = True

        if len(self.metadata) == 0:
            dictionary['first'] = True

        self.metadata += [dictionary]

    def extend(self, listing, **kwargs):
        """
        Extends `self.metadata` with data built from passed `listing`

        - Returns: `None`

        > `kwargs` is passed to `MetaList` when transmuting list types
        """
        super(MetaList, self).extend(listing)
        for index, value in enumerate(listing):
            if isinstance(value, list):
                last_address = []
                if self.metadata:
                    # Grab `address` list minus last item
                    last_address = self.metadata[-1]['address'][0:-1]
                # Add this `index` to `address` list for recursing
                sub_list = MetaList(value, address = last_address + [index], **kwargs)
                self.append(sub_list, **kwargs)
            else:
                self.append(value, **kwargs)

    def insert(self, index, item, **kwargs):
        """
        Inserts `item` at `index` for `self` and dictionary into `self.metadata`

        - Returns: `None`

        Note: `self.metadata[index + 1]` have the following data mutated

        - `data['index']`
        - `data['address']`

        Additionally: `self.metadata[0]` and `self.metadata[-1]` data mutations will occur

        - `data['first']`
        - `data['last']`
        """
        super(MetaList, self).insert(index, item)

        address = kwargs.get('address', []) + [index]
        dictionary = self.__return_dictionary(
            address = address,
            index = index,
            value = item,
            **kwargs)
        self.metadata.insert(index, dictionary)

        self.__refresh_first()
        self.__refresh_last()
        self.__refresh_indexes(start = index)
        # Off-set to avoid n +- 1 errors ;-)
        self.__refresh_addresses(
            start = index + 1,
            index = len(address) - 1,
            modifier = 1)

    def pop(self, index = -1, target = None):
        """
        Pop value from `self` and `self.metadata`, at `index`

        - Returns: `self.pop(i)` or `self.metadata.pop(i)` depending on `target`
        """
        popped_self = super(MetaList, self).pop(index)
        popped_meta = self.__pop_metadata(index)

        if 'metadata' in target.lower():
            return popped_meta

        return popped_self

    def remove(self, value):
        """
        Removes `value` from `self` and `self.metadata` lists

        - Returns: `None`
        - Raises: `ValueError` if value does not exsist within `self` or `self.metadata` lists
        """
        super(MetaList, self).remove(value)
        productive = False
        for data in self.metadata:
            if data['value'] == value:
                productive = True
                self.__pop_metadata(data['index'])
                break

        if not productive:
            raise ValueError("value not found in MetaList.metadata values")

    # Special herbs and spices for keeping the metadata fresh
    def __pop_metadata(self, index = -1):
        """
        Pops `index` from `self.metadata` listing, last item if no `index` was passed

        - Returns: `<dictionary>`
        - Raises: `IndexError` if `index` is outside of listed range
        """
        popped_metadata = self.metadata.pop(index)
        addr_index = len(popped_metadata['address']) - 1

        ## Update values within `self.metadata` dictionaries
        self.__refresh_first()
        self.__refresh_last()
        self.__refresh_indexes(start = index)
        self.__refresh_addresses(start = index, index = addr_index, modifier = -1)

        return popped_metadata

    def __return_dictionary(self, address, index, value, **kwargs):
        """
        Returns dictionaries for use in `self.metadata` that contains;

        - `address`: list of indexes leading to nested value, eg `[0, 4, 2]`
        - `index`: integer of where value is stored in current listing
        - `value`: Duck!... Note list types will be converted to `MetaList`
        - `first`: boolean `False` by default
        - `last`: boolean `False` by default

        > `kwargs`: passes through to `MetaList` if transmuting a list `value`
        """
        if isinstance(value, list):
            kwargs['address'] = address
            value = MetaList(value, **kwargs)

        dictionary = OrderedDict()
        dictionary['address'] = address
        dictionary['index'] = index
        dictionary['value'] = value
        dictionary['first'] = False
        dictionary['last'] = False
        return dictionary

    def __refresh_indexes(self, start = 0):
        """
        Update indexes from `start` till the last

        - Returns: `None`
        """
        for i in range(start, len(self.metadata)):
            self.metadata[i]['index'] = i

    def __refresh_addresses(self, start = 0, end = None, index = 0, modifier = -1):
        """
        Updates `address`es within `self.metadata` recursively

        - Returns: `None`
        - Raises: `TODO`

        > `index` is the *depth* within `address` that `modifier` will be applied to
        """
        if not start or start < 0:
            start = 0
        if not end or end > len(self.metadata):
            end = len(self.metadata)

        for i in range(start, end):
            metadata = self.metadata[i]
            if isinstance(metadata['value'], list):
                metadata['value'].__refresh_addresses(index = index, modifier = modifier)
            else:
                if len(metadata['address']) - 1 >= index:
                    metadata['address'][index] += modifier
                else:
                    raise Exception("# TODO: __refresh_addresses append or extend address list")

    def __refresh_last(self, quick = True):
        """
        Sets/re-sets `self.metadata` `last` value

        - Returns `True`/`False` based on if `self.metadata` was touched

        If `quick` is `False` all items in current listing will be touched
        If `quick` is `True` only the last item and second to last items are touched
        """
        if not self.metadata:
            return False

        if len(self.metadata) > 1:
            self.metadata[-2]['last'] = False

        if not quick and len(self.metadata) > 1:
            for i in range(0, len(self.metadata) - 1):
                self.metadata[i]['last'] = False

        self.metadata[-1]['last'] = True
        return True

    def __refresh_first(self, quick = True):
        """
        Sets first dictionary within `self.metadata` `first` key to `True`

        - Returns `True`/`False` based on if `self.metadata` was touched

        If `quick` is `False` all items will be touched in current listing
        If `quick` is `True` the first and second items are updated
        """
        if not self.metadata:
            return False

        if len(self.metadata) > 1:
            self.metadata[1]['first'] = False

        if not quick and len(self.metadata) > 1:
            for i in range(1, len(self.metadata)):
                self.metadata[i]['first'] = False

        self.metadata[0]['first'] = True
        return True

    # Stuff to play with
    def deep_get(self, indexes, iterable = None):
        """
        Loops over `indexes` returning inner list or value from `self.metadata`

        - `indexes` list of indexes, eg `[1, 3, 2]`
        - `iterable` maybe list, if not provided `self.metadata` is searched
        """
        referance = self.metadata
        if iterable:
            reference = iterable

        for index in indexes:
            reference = reference[index]

        return reference

    def copy_metadata(self):
        """
        Returns copy of `self.metadata`
        """
        return list(self.metadata)

    def yield_metadata(self, iterable = None, skip = {'first': False, 'last': False, 'between': False}, **kwargs):
        """
        Yields a *flat* representation of `self.metadata`,

        Prefilter via `skip = {}` dictionary with the following data

        - `first`: boolean, if `True` skips items that are first
        - `last`: boolean, if `True` skips items that are last
        - `between`: boolean, if `True` skips items that are not last or first
        """
        metadata = self.metadata
        if iterable:
            metadata = MetaList(iterable).metadata

        for item in metadata:
            if isinstance(item.get('value'), list):
                # Recurse thy self
                for data in item['value'].yield_metadata(skip = skip, **kwargs):
                    yield data
            else:
                if skip:
                    if skip.get('first', False) and item['first']:
                        continue
                    if skip.get('last', False) and item['last']:
                        continue
                    if skip.get('between', False) and not item['first'] and not item['last']:
                        continue
                # If not skipped get to yielding
                yield item

... y tal vez es más buggug que las lámparas de ese amigo que habló públicamente de encuentros cercanos, saben quiénes son ... pero esto hace algunos trucos ingeniosos

Ejemplo de entrada uno

meta_list = MetaList([1, 2, 3, 4, 5])

for data in meta_list.metadata:
    if data['first']:
        continue
    if data['last']:
        continue

    print("self[{0}] -> {1}".format(data['index'], data['value']))

Ejemplo de salida uno

self[1] -> 2
self[2] -> 3
self[3] -> 4

Ejemplo de entrada dos

meta_list = MetaList(['item one', ['sub item one', ('sub', 'tuple'), [1, 2, 3], {'key': 'val'}], 'item two'])

for data in meta_list.yield_metadata():
    address = "".join(["[{0}]".format(x) for x in data.get('address')])
    value = data.get('value')
    print("meta_list{0} -> {1} <- first: {2} | last: {3}".format(address, value, data['first'], data['last']))

Ejemplo de salida dos

meta_list[0] -> item one <- first: True | last: False
meta_list[1][0] -> sub item one <- first: True | last: False
meta_list[1][1] -> ('sub', 'tuple') <- first: False | last: False
meta_list[1][2][0] -> 1 <- first: True | last: False
meta_list[1][2][1] -> 2 <- first: False | last: False
meta_list[1][2][2] -> 3 <- first: False | last: True
meta_list[1][3] -> {'key': 'val'} <- first: False | last: True
meta_list[2] -> item two <- first: False | last: True

Si sientes que tus cerebros se vuelven frescos, pero no está totalmente bien y eso está a su manera mejor ... para mí, ese es el más Pitónico

Disfruta, y tal vez si hay interés, empujaré esto a Github para que todos tiren y tengan un bifurcado.

Nota al margen @fabriziom +1 para un uso excelente de @property mágica

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top