Flask SQLAlchemy Mapeador de Dados vs Padrão de Registro Ativo

11

Comecei recentemente a trabalhar no Flask e no Flask-SQLAlchemy. Vindo do background do Django, achei o Flask-SQLAlchmey bastante complexo. Eu li que o SQLAlchemy implementa o padrão Data Mapper enquanto o Django ORM é baseado no Active Record Pattern.

Aqui é um código de exemplo escrito que implementa o padrão de repositório para acessar o banco de dados.

aqui é outro link de um comentário de S.Lott (reputação de 271k) que diz que ORM é a camada de acesso a dados e é separado do modelo.

Minhas perguntas são estas:

  1. Você pode fornecer um caso de uso prático no exemplo acima ou um exemplo próprio em que o padrão do Mapeador de dados é útil? Em todos os lugares que li, o padrão de mapeamento de dados é útil em situações complexas, mas não exemplos.
  2. O uso de padrões de repositórios, como no caso acima, é igual ao uso de um padrão de mapeamento de dados?
  3. O mapeador de dados defende escrever consultas selecionadas em uma classe diferente do modelo, como feito no exemplo?
  4. Por que Question.query.filter_by(text = text).all() não é melhor usar do que db.session.query(Question).filter(Question.text == text).all() ?

Esta não é uma duplicata de DataMapper vs ActiveRecord padrão porque isso apenas diz a definição, estou mais interessado nos exemplos práticos.

    
por Anubhav Agarwal 24.02.2017 в 20:57
fonte

2 respostas

3

Ponto por ponto.

1.

Eu tenho um banco de dados legado para o qual eu tenho que escrever alguns utilitários de manipulação de dados. Usar o padrão Mapper, sem o estilo ORM / ActiveRecord, tornava as coisas fáceis para mim ao escrever consultas como o ActiveRecord faria. Ele está operando em objetos compostos agradáveis que se assemelham a cláusulas SQL, protegidos de injeções SQL.

Objetos sendo 'passivos' permitiram mais flexibilidade / uniformidade: um resultado de uma junção complexa é uma tupla nomeada, como resultado de uma simples seleção. Não há identidade para se preocupar, nenhum objeto em cache com a mesma identidade.

Todas as atualizações são explícitas; não um "save" de algum estado alterado em outro lugar, nenhum hook rodando em .save() , etc. Isso tornou as atualizações de batch eficientes triviais, sem incomodar se os dados corretos forem enviados para o DB. Ambos foram benefícios no meu caso. Em geral, 'depende'. Por exemplo, tive que buscar manualmente IDs gerados pelo banco após inserções. Executar essa consulta explicitamente é um pouco de trabalho extra. Ser capaz de fazer isso em uma consulta, em vez de uma por registro, foi uma grande vantagem no meu caso.

O SQLAlchemy tem um design em camadas que permite que você acesse o nível "mapeador" mais baixo, mesmo que você declare coisas no nível ORM superior e opere normalmente nele. No Django, por exemplo, não é tão simples se / quando ainda é possível.

2.

No exemplo, o 'repositório' parece um nível construído acima do 'mapeador'. O repositório poderia ter sido construído sobre o DBAPI simples, mas o mapeador torna algumas coisas mais simples, como ligação de parâmetro mais agradável, chamadas tuplas para os conjuntos de resultados e um wrapper acima de SQL simples com partes componíveis e reutilizáveis.

O mapeador também fornece um certo grau de independência do banco de dados. Por exemplo. O SQL Server e o Postgres têm maneiras diferentes de concatenar cadeias de caracteres; o mapeador fornece uma interface unificada.

3.

Você escreve seu select onde você o usa. Se você tem um select que você reutiliza constantemente em diferentes contextos, você pode colocá-lo em um método ou função. A maioria dos selects tem um uso e são construídos no local.

Um recurso interessante do design do SQLAlchemy é que você pode facilmente armazenar condições e cláusulas where inteiras e reutilizá-las em instruções select / update / delete.

4.

Question.query.filter_by(text = text).all() usa uma transação implícita. db.session.query(Question).filter(Question.text == text).all() usa uma transação explícita.

As transações explícitas proporcionam tranquilidade com o DML. Eles são importantes com select s, também, quando você está consultando um banco de dados que muda rapidamente e deseja que seus vários select s vejam o mesmo estado consistente.

Eu costumo escrever um wrapper trivial em torno de sessionmaker e escrever coisas assim:

with my_database.transaction() as trans:
   records = trans.query(...)
   ...
   updated = trans.execute(...).rowcount
# Here the transaction commits if all went well.

Quando eu definitivamente sei que nenhuma DML deve ser executada neste bloco, eu uso .readonly_transaction() que sempre reverte.

Em muitos casos, a transação implícita está bem. O Django permite que você decore um método com @transaction.atomic e tenha um controle de transação semi-explícito, suficiente em 99% dos casos. Mas às vezes você precisa de granularidade ainda mais fina.

    
por 9000 05.03.2017 / 22:34
fonte
2

Concorda totalmente com a resposta acima: sim, o padrão Data Mapper do SQLAlchemy é realmente mais flexível e, para consultas complexas, é realmente mais poderoso, menos mágico e mais controlado.

Mas, em tarefas simples como CRUD , o código do SQLAlchemy fica muito acima do peso / excessivo / redundante.

Por exemplo, para criar apenas um objeto no controlador "create" mais simples, você precisa de algo assim:

user = User(name='Nick', surname='Nickson')
session.add(user)
session.flush()

Enquanto estiver no Active Record ORM, você só precisará de uma única string.

Bem, para tarefas simples , alguns de nós podem querer algo mais simples. Quero dizer, será legal ter Active Record for SQLAlchemy.

Boas notícias: Eu criei recentemente um pacote para isso (ele também contém outras coisas úteis).

Confira: link

    
por Alexander Litvinenko 31.03.2017 / 08:10
fonte