![]()
O Problema Link para o cabeçalho
Imagine que você está integrando um produto novo no seu sistema. Este produto deve possuir detalhes e especificações completos antes de ser publicado. Porém, essas informações são adicionadas em passos separados e não necessariamente ordenados. Como verificar que todos os passos foram completados?
Uma solução ingênua seria usar variáveis booleanas para cada passo:
class ProductTrack(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
has_specification = models.BooleanField(default=False)
has_detail = models.BooleanField(default=False)
def is_complete(self):
return self.has_specification and self.has_detail
Mas e se você precisar adicionar mais passos? Ou remover algum? Cada alteração exige mudanças no modelo, migrações de banco de dados e ajustes na lógica.
A Solução: IntFlag Link para o cabeçalho
Python oferece a classe IntFlag do módulo enum que permite usar máscaras de bits de forma elegante e legível. A ideia é representar cada passo como um bit diferente em um número inteiro.
from enum import IntFlag, auto
class ProductInfo(IntFlag):
NONE = 0
HAS_SPECIFICATION = auto()
HAS_DETAIL = auto()
ALL_INFO = HAS_SPECIFICATION | HAS_DETAIL
class ProductTrack(models.Model):
product = models.ForeignKey(
Product,
on_delete=models.CASCADE,
related_name='track',
verbose_name='Produto',
)
flag = models.IntegerField(default=ProductInfo.NONE.value)
def __str__(self):
return f'Rastreamento de {self.product.name}'
def flag_as(self, flag):
self.flag |= flag
def has_flag(self, flag):
return bool(self.flag & flag)
def is_complete(self):
return self.flag == ProductInfo.ALL_INFO
Como Funciona? Link para o cabeçalho
Máscaras de Bits Link para o cabeçalho
Cada valor do enum representa uma potência de 2 em binário:
# auto() gera automaticamente os valores
HAS_SPECIFICATION = 1 # binário: 0001
HAS_DETAIL = 2 # binário: 0010
Quando combinamos valores usando o operador | (OR bit a bit), estamos “ligando” múltiplos bits:
ALL_INFO = HAS_SPECIFICATION | HAS_DETAIL # binário: 0011 (decimal: 3)
Operações com IntFlag Link para o cabeçalho
O método flag_as usa o operador |= (OR-igual) para adicionar um flag sem remover os existentes:
track.flag_as(ProductInfo.HAS_SPECIFICATION)
# Se flag era 0 (0000), agora é 1 (0001)
track.flag_as(ProductInfo.HAS_DETAIL)
# Se flag era 1 (0001), agora é 3 (0011)
Para verificar se um flag específico está ativo, você pode usar o método has_flag:
if track.has_flag(ProductInfo.HAS_SPECIFICATION):
print("Possui especificação!")
Vantagens Link para o cabeçalho
- Flexibilidade: Adicionar novos passos é trivial - basta incluir uma nova linha no enum:
class ProductInfo(IntFlag):
NONE = 0
HAS_SPECIFICATION = auto()
HAS_DETAIL = auto()
HAS_IMAGE = auto() # Novo passo!
ALL_INFO = HAS_SPECIFICATION | HAS_DETAIL | HAS_IMAGE
Eficiência: Apenas um campo inteiro no banco de dados ao invés de múltiplos booleanos.
Legibilidade: O código é expressivo e auto-documentado.
Compatibilidade: Funciona com qualquer framework (Django, Flask, etc) ou sem framework algum.
Considerações Finais Link para o cabeçalho
Embora o exemplo utilize Django, a solução é genérica e pode ser aplicada em qualquer contexto Python onde você precise rastrear estados ou progresso. IntFlag é uma ferramenta poderosa que combina a eficiência de máscaras de bits com a legibilidade de enumerados.
Então é isso pessoal!
Até a próxima!
{}’s