Exercícios da aula 04
Exercício 01
Fazer uma alteração no modelo (tabela User
) e adicionar um campo chamado updated_at
:
- Esse campo deve ser mapeado para o tipo
datetime
- Esse campo não deve ser inicializado por padrão
init=False
- O valor padrão deve ser
now
- Toda vez que a tabela for atualizada esse campo deve ser atualizado:
Solução
@table_registry.mapped_as_dataclass
class User:
__tablename__ = 'users'
id: Mapped[int] = mapped_column(init=False, primary_key=True)
username: Mapped[str] = mapped_column(unique=True)
password: Mapped[str]
email: Mapped[str] = mapped_column(unique=True)
created_at: Mapped[datetime] = mapped_column(
init=False, server_default=func.now()
)
updated_at: Mapped[datetime] = mapped_column( # Exercício
init=False, server_default=func.now(), onupdate=func.now()
)
Exercício 02
Altere o evento de testes (mock_db_time
) para ser contemplado no mock o campo updated_at
na validação do teste.
Solução
A ideia é adicionar mais um campo na verificação do modelo, para que o update também esteja um horário determinístico:
@contextmanager
def _mock_db_time(*, model, time=datetime(2024, 1, 1)):
def fake_time_handler(mapper, connection, target):
if hasattr(target, 'created_at'):
target.created_at = time
if hasattr(target, 'updated_at'):
target.updated_at = time
event.listen(model, 'before_insert', fake_time_handler)
yield time
event.remove(model, 'before_insert', fake_time_handler)
Com a alteração do modelo, o teste também passará a falhar. Isso pode ser modificado adicionando o campo updated_at
no dicionário de validação:
def test_create_user(session, mock_db_time):
with mock_db_time(model=User) as time:
new_user = User(
username='alice', password='secret', email='teste@test'
)
session.add(new_user)
session.commit()
user = session.scalar(select(User).where(User.username == 'alice'))
assert asdict(user) == {
'id': 1,
'username': 'alice',
'password': 'secret',
'email': 'teste@test',
'created_at': time,
'updated_at': time,
}
Exercício 03
Criar uma nova migração autogerada com alembic.
Solução
Comando explicado na aula para gerar uma migração automática:
O Comando deve retornar algo parecido com isso:
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.autogenerate.compare] Detected added column 'users.updated_at'
Generating /home/dunossauro/git/fastapi-do-
zero/codigo_das_aulas/04/migrations/versions/bb77f9679811_exercicio_02_aula_04.py ... done
O arquivo de migrações deve se parecer com esse:
- Adiciona a coluna
updated_at
na tabelausers
- Remove a coluna
updated_at
na tabelausers
Exercício 04
Aplicar essa migração ao banco de dados
Solução
Para aplicar a ultima migração devemos nos mover até a head:
alembic upgrade head
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running upgrade 74f39286e2f6 -> bb77f9679811, exercicio 02 aula 04
Checando o resultado no schema do banco de dados:
sqlite3 database.db
SQLite version 3.46.1 2024-08-13 09:16:08
Enter ".help" for usage hints.
sqlite> .schema
CREATE TABLE alembic_version (
version_num VARCHAR(32) NOT NULL,
CONSTRAINT alembic_version_pkc PRIMARY KEY (version_num)
);
CREATE TABLE users (
id INTEGER NOT NULL,
username VARCHAR NOT NULL,
password VARCHAR NOT NULL,
email VARCHAR NOT NULL,
created_at DATETIME DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
updated_at DATETIME DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
PRIMARY KEY (id),
UNIQUE (email),
UNIQUE (username)
);
Podemos ver que o campo updated_at
foi criado com o tipo DATETIME
e com o valor padrão para CURRENT_TIMESTAMP
, assim como no created_at
.