Tabelas
Sua automação pode guardar dados na plataforma: você declara as tabelas no código, o datallog push leva a declaração junto e a plataforma cria o que falta. Não há console de banco, string de conexão nem migration para escrever.
Precisa do pacote datallog 0.4.21 ou mais novo.
Declarar
from datallog import table, Text, Number, Date, Bool, Json
leads = table("leads", fields={
"company": Text(),
"stage": Text(index=True),
"value": Number(),
"next_action": Date(),
"active": Bool(),
"payload": Json(),
})table() roda na sua máquina, valida na hora e devolve a declaração como dado puro. Declare no topo do arquivo da automação, ou num módulo que elas importem — o que importa é que a linha execute quando o código é carregado.
Os tipos
| Tipo | Guarda | Vira, no banco |
|---|---|---|
Text() | texto de qualquer tamanho | text |
Number() | número exato, inclusive dinheiro | numeric |
Date() | data, sempre em YYYY-MM-DD | date |
Bool() | verdadeiro ou falso | boolean |
Json() | objeto ou lista inteiros | jsonb |
index=True cria um índice naquela coluna. Use nas que você filtra ou ordena; cada índice ocupa espaço e custa em toda escrita, então não é para todas.
Toda tabela ganha uma coluna id automática, que você não declara e não escreve.
Os nomes
Nome de tabela e de coluna aceita letras minúsculas, números e _, começando por letra, até 63 caracteres. São reservados id, order_by, limit, offset e columns como coluna, e tables e schema como tabela.
As chaves são em inglês de propósito: elas atravessam a rede e aparecem na tela da plataforma.
Publicar
datallog pushA saída diz quantas tabelas achou:
Found 2 declared tablesA declaração é aditiva: tabela nova é criada, coluna nova é acrescentada, e coluna que você tirou da declaração não é apagada — o dado dela continua lá. Para remover de verdade, é pedido nosso, de propósito: push a partir de um checkout velho não pode apagar coluna.
Publicar duas vezes a mesma declaração não faz nada na segunda: o que já existe fica como está.
Dois tetos valem por push: 1599 colunas por tabela (o Postgres recusa mais de 1600, e uma é o id) e 200 tabelas por declaração.
O que a plataforma recusa, e o que você captura
A mesma validação roda dos dois lados — na sua máquina, para o erro apontar o seu arquivo, e no servidor, porque o que chega pela rede é o que alguém escolheu mandar. Os erros são importáveis:
from datallog import InvalidIdentifier, InvalidManifest, UnknownType, TableRedeclared
try:
table("Leads!", fields={"company": Text()})
except InvalidIdentifier as erro:
print(erro)
# invalid table name: 'Leads!'. Use only lowercase letters, digits and _,
# starting with a letter, up to 63 characters.| Erro | Quando |
|---|---|
InvalidIdentifier | nome de tabela ou coluna fora da regra, ou reservado |
UnknownType | tipo que não existe ("Texto", "string") |
InvalidManifest | forma errada: tabela sem campo, campo repetido, teto estourado |
TableRedeclared | a mesma tabela declarada duas vezes com campos diferentes |
DataEngineError | a base de todos, para capturar de uma vez |
Declare a tabela em um lugar só
Duas automações que declaram leads com os mesmos campos não dão problema — é copiar e colar, e a plataforma trata as duas como uma. Com campos diferentes, sai TableRedeclared: aí extraia a declaração para um módulo e importe nas duas.
Mudar uma tabela que já existe
Acrescentar coluna é só declarar e dar push. Trocar o tipo de uma coluna que já tem dado é recusado — mudar Text para Number com linha gravada perde dado, e a plataforma prefere dizer isso a converter no escuro:
the manifest changes the type of a column that already exists, and changing
type with data inside loses data: leads.value is text in the database and
numeric in the manifestO caminho é declarar outra coluna com o tipo certo, migrar o conteúdo pela sua automação e parar de escrever na antiga.
Gravar e ler
A tabela que table() devolve é o que você usa dentro da automação. Não há URL para montar nem credencial para passar: ela já sabe falar com a plataforma.
from datallog import automation, table, Text, Number, Date
leads = table("leads", fields={
"company": Text(),
"stage": Text(index=True),
"value": Number(),
"next_action": Date(),
})
@automation()
def roda(seed=None):
lead = leads.insert({"company": "Acme", "stage": "aberto", "value": 4500})
leads.update(lead["id"], stage="em conversa")
return leads.find(stage="em conversa", limit=20)| Operação | O que faz |
|---|---|
insert(linha) | grava uma linha e devolve ela inteira, com o id |
insert_many(linhas) | grava um lote e devolve só os id |
find(**filtros) | as linhas que casam, como lista de dicionários |
find_with_total(**filtros) | (linhas, total), para paginar mostrando "31 de 1.200" |
get(id) | a linha, ou None se ela não existe |
count(**filtros) | quantas linhas casam, sem trazer nenhuma |
update(id, **campos) | muda os campos dados e devolve a linha atualizada |
delete(id) | apaga; True se apagou, False se já não existia |
aggregate(...) | conta, soma, média, mínimo e máximo, com group by |
Filtrar
Filtro é coluna=valor, e None procura quem está vazio:
leads.find(stage="aberto") # igualdade
leads.find(stage="aberto", company="Acme") # as duas condições
leads.find(next_action=None) # sem data marcada
leads.count(stage="perdido")limit, offset, order_by e columns são parâmetros, não filtros — e isso nunca é ambíguo, porque o motor reserva esses quatro nomes como coluna:
leads.find(stage="aberto", order_by="-value", limit=50, offset=100)
leads.find(columns=["company", "value"]) # só o que a tela usacolumns é a maior economia da rota
Medido no porte do CRM: um kanban precisava de 11.859 bytes e recebia 643.514 — 54 vezes mais — porque a tabela tinha uma coluna Json grande que ninguém ia desenhar. Peça as colunas que você usa.
Contas no banco, não na automação
leads.aggregate(
[{"function": "sum", "column": "value", "as": "total"},
{"function": "count", "as": "quantos"}],
group_by="stage",
)
# [{"stage": "aberto", "total": 91500, "quantos": 20}, ...]Somar mil linhas trazendo as mil é pagar banda e memória por um número.
Quando algo dá errado
from datallog import InvalidValue, TableNotFound, RowNotFound
try:
leads.insert({"value": "R$ 4.500,00"})
except InvalidValue as erro:
print(erro) # Postgres refused a value in this operation: … send numbers
# without currency symbols or thousand separators …get e delete já tratam a linha que sumiu — devolvem None e False. update numa linha apagada por outra pessoa levanta RowNotFound.
Fora da plataforma não funciona, e diz isso
A credencial vem do ambiente da execução. Rodando na sua máquina, qualquer operação levanta "no execution credential: reading and writing tables only works inside a Datallog automation".
Em projeto que roda contra outro ambiente que não produção, ponha datallog_api_url nas variáveis do projeto — sem isso a automação fala com a conta de produção.
Onde ver
No painel, em Uso → Dados, aparecem as tabelas da conta, o tamanho de cada uma, o teto do plano e o custo do dia.