<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Wesley de Morais</title>
    <description>The latest articles on DEV Community by Wesley de Morais (@wesleymorais).</description>
    <link>https://dev.to/wesleymorais</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F882414%2F62ed2664-c620-4993-a4b7-50ffe3d2a859.jpeg</url>
      <title>DEV Community: Wesley de Morais</title>
      <link>https://dev.to/wesleymorais</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wesleymorais"/>
    <language>en</language>
    <item>
      <title>RAG na prática: transformando PDFs em respostas inteligentes com LLMs</title>
      <dc:creator>Wesley de Morais</dc:creator>
      <pubDate>Fri, 13 Jun 2025 19:50:55 +0000</pubDate>
      <link>https://dev.to/wesleymorais/rag-na-pratica-transformando-pdfs-em-respostas-inteligentes-com-llms-5bh0</link>
      <guid>https://dev.to/wesleymorais/rag-na-pratica-transformando-pdfs-em-respostas-inteligentes-com-llms-5bh0</guid>
      <description>&lt;h2&gt;
  
  
  Sumário
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Entendendo o que são Embeddings&lt;/li&gt;
&lt;li&gt;Entendendo o que são Vector Store&lt;/li&gt;
&lt;li&gt;Fluxo de uma aplicação normal com  RAG&lt;/li&gt;
&lt;li&gt;Fluxo da nossa aplicação usando RAG&lt;/li&gt;
&lt;li&gt;Criando nossa aplicação&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Entendendo o que são Embeddings
&lt;/h3&gt;

&lt;p&gt;Modelos de inteligência artificial não compreendem diretamente a linguagem natural como os humanos. Em vez disso, eles trabalham melhor com representações matemáticas. É aí que entram os &lt;strong&gt;embeddings&lt;/strong&gt; — vetores gerados por modelos de &lt;em&gt;embedding&lt;/em&gt; que capturam a essência de um texto, imagem ou áudio.&lt;/p&gt;

&lt;p&gt;Esses vetores tornam possível comparar conteúdos de forma rápida e precisa, permitindo que sistemas de IA reconheçam similaridades mesmo quando a linguagem usada não é exatamente a mesma.&lt;/p&gt;

&lt;h3&gt;
  
  
  Entendendo o que são Vector Store
&lt;/h3&gt;

&lt;p&gt;Os &lt;strong&gt;vector stores&lt;/strong&gt; são bancos de dados especializados em armazenar esses vetores (&lt;em&gt;embedding vectors&lt;/em&gt;).Quando um novo vetor é consultado, o vector store retorna os vetores armazenados mais semelhantes a ele, com base em medidas matemáticas de similaridade.&lt;/p&gt;

&lt;p&gt;Esses vetores recuperados podem então ser usados como &lt;strong&gt;contexto&lt;/strong&gt; para um modelo de linguagem (LLM), que gera uma resposta em linguagem natural com base nas informações mais relevantes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fluxo de uma aplicação normal com  RAG(Retrieval-augmented generation)
&lt;/h3&gt;

&lt;p&gt;Aqui temos um fluxo de uma aplicação básica usando RAG&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Um &lt;strong&gt;Vector Store&lt;/strong&gt; é preenchido previamente com dados que foram convertidos em &lt;strong&gt;embedding vectors&lt;/strong&gt; e armazenados para busca eficiente.&lt;/li&gt;
&lt;li&gt;Quando um usuário faz uma pergunta, essa pergunta é convertida em um &lt;strong&gt;embedding vector&lt;/strong&gt; correspondente.&lt;/li&gt;
&lt;li&gt;Esse vetor da pergunta é enviado ao Vector Store, que utiliza algoritmos de similaridade para recuperar os &lt;strong&gt;embedding vectors mais similares&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Os vetores recuperados são mapeados de volta para seus respectivos &lt;strong&gt;Documents (textos originais)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Esses Documents relevantes são inseridos em um &lt;strong&gt;prompt estruturado&lt;/strong&gt;, junto com a pergunta do usuário.&lt;/li&gt;
&lt;li&gt;O prompt é enviado como contexto para uma &lt;strong&gt;LLM (Large Language Model)&lt;/strong&gt;, que utiliza as informações para gerar uma resposta.&lt;/li&gt;
&lt;li&gt;A LLM gera o texto (recurso) como resposta ao usuário, combinando a informação do contexto com sua capacidade de linguagem natural.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu0nid0asdy7yklhb1ge5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu0nid0asdy7yklhb1ge5.png" alt="flow_rag_normal" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Fluxo da nossa aplicação usando RAG
&lt;/h3&gt;

&lt;p&gt;No nosso RAG, vamos seguir uma abordagem parecida, mas com uma diferença: em vez de a aplicação apenas ficar esperando perguntas baseadas em informações previamente carregadas, vamos permitir que o usuário envie um arquivo junto com a pergunta&lt;/p&gt;

&lt;p&gt;Dessa forma, a aplicação poderá entender o contexto diretamente a partir do conteúdo enviado, respondendo com base naquele documento.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F819r71nyktxafziqikzo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F819r71nyktxafziqikzo.png" alt="flow_our_app" width="800" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O arquivo em pdf vai ser dividido em partes usando a classe &lt;strong&gt;RecursiveCharacterTextSplitter&lt;/strong&gt; e para cada parte vai ser criada um embedding vector relacionado.&lt;/p&gt;

&lt;h2&gt;
  
  
  Criando nossa aplicação
&lt;/h2&gt;

&lt;p&gt;Antes de começar nossa aplicação é necessário que crie um arquivo &lt;code&gt;.env&lt;/code&gt; contendo essas informações&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;your_openai_api_key&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;OPENAI_ORGANIZATION_KEY&lt;/span&gt;&lt;span class="o"&gt;=&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;your_openai_api_key&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Você pode pegar essas informações acessando o link &lt;a href="https://platform.openai.com/docs/overview" rel="noopener noreferrer"&gt;https://platform.openai.com/docs/overview&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependências
&lt;/h3&gt;

&lt;p&gt;Com o ambiente virtual criado instale as seguintes  dependências&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;langchain==0.3.25
gradio==5.33.2
langchain-openai==0.3.22
langchain-community==0.3.25
pypdf==5.6.0
python-dotenv==1.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nossa aplicação vai seguir 2 etapas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Criação da interface demo usando a biblioteca gradio&lt;/li&gt;
&lt;li&gt;Criação da caso de uso para executar nossa aplicação&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Criação da interface demo
&lt;/h3&gt;

&lt;p&gt;Em um arquivo chamado app.py podemos adicionar seguinte código:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import gradio as gr

def handle_file(text_input, file):
    return text_input


with gr.Blocks() as demo:
    gr.Markdown("## Verificador de PDFs com RAG")
    text_input = gr.Textbox(label="Entre com sua questão aqui", placeholder="Faça uma pergunta sobre o PDF carregado")
    upload = gr.File(type="filepath")
    output = gr.Textbox(label="Output")
    button = gr.Button("Submit")

    button.click(
        fn=handle_file,
        inputs=[text_input, upload],
        outputs=output
    )
demo.launch(share=True)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O código acima é responsável por construir uma interface onde teremos :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Um campo para o usuário adicionar a sua questão&lt;/li&gt;
&lt;li&gt;Um campo para adicionar o arquivo&lt;/li&gt;
&lt;li&gt;Um campo que será adicionado a resposta da llm&lt;/li&gt;
&lt;li&gt;Um botão de submissão, sendo responsável por quando for feito o evento de clique, então ser chamado a função &lt;code&gt;handle_file&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No final ele expõe essa interface para ser acessado externamente.&lt;/p&gt;

&lt;h3&gt;
  
  
  Criação da caso de uso para executar nossa aplicação
&lt;/h3&gt;

&lt;p&gt;No mesmo arquivo podemos criar um caso de uso onde inicialmente ele vai pegar o arquivo enviado e carregar na variável &lt;code&gt;self.docs&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class HandleLLMUseCase:

    def handle_pdf_load(self):
        """
        Carrega o PDF enviado e extrai os documentos.
        Se nenhum arquivo for enviado, levanta um erro.
        """
        if self.file is None:
            raise ValueError("Nenhum arquivo enviado.")

        loader = PyPDFLoader(self.file)
        self.docs = loader.load()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com o arquivo carregado, podemos dividir seu conteúdo em pequenos trechos (chunks), o que facilita a criação dos embedding vectors posteriormente.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def handle_text_split(self):
        """
        Divide os documentos carregados em chunks menores de 
        500 caracteres com sobreposição de 50 caracteres.
        Se nenhum documento for carregado, levanta um erro.
        """
        if not hasattr(self, 'docs'):
            raise ValueError("Nenhum documento carregado.")

        text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
        self.chunks = text_splitter.split_documents(self.docs)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com os chunks de texto já criados, podemos convertê-los em embedding vectors utilizando o modelo OpenAIEmbeddings. Esses vetores serão então armazenados em um Vector Store, que permite realizar buscas por similaridade de forma eficiente.  Para fins de simplicidade e praticidade, vamos utilizar o InMemoryVectorStore, uma opção gratuita e fácil de usar.&lt;/p&gt;

&lt;p&gt;No entanto, o Langchain oferece suporte a diversos outros vector stores, como FAISS, Chroma, Weaviate, Pinecone e outros — você pode conferir a lista completa na &lt;a href="https://python.langchain.com/docs/integrations/vectorstores/" rel="noopener noreferrer"&gt;Documentação oficial&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def handle_embeddings(self):
        """
        Cria embeddings dos chunks de texto usando OpenAIEmbeddings .
        Se nenhum chunk de texto estiver disponível, levanta um erro.
        """
        if not hasattr(self, 'chunks'):
            raise ValueError("Nenhum chunk de texto disponível.")

        embeddings = OpenAIEmbeddings(openai_api_key=os.environ.get("OPENAI_API_KEY"))
        vectorstore = InMemoryVectorStore.from_documents(self.chunks, embeddings)
        self.retriever = vectorstore.as_retriever()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com o Vector Store já criado, podemos agora construir uma cadeia de RAG que combina os documentos recuperados do Vector Store com uma LLM. Essa cadeia será responsável por buscar os trechos mais relevantes no conteúdo armazenado e usá-los como contexto para a LLM gerar uma resposta em linguagem natural.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def handle_chain_creation(self):
        """
        Cria uma cadeia de perguntas e respostas (RetrievalQA) usando o modelo LLM.
        """
        llm = ChatOpenAI(
            api_key=os.environ.get("OPENAI_API_KEY"),
            organization=os.environ.get("OPENAI_ORGANIZATION_KEY"),
            model="gpt-4o-mini",
        )

        self.qa_chain = RetrievalQA.from_chain_type(
            llm=llm,
            retriever=self.retriever
        )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def handle_query(self):
        """
        Executa a consulta na cadeia de perguntas e respostas e retorna o resultado.
        """
        response = self.qa_chain.invoke({"query": self.text_input})
        return response["result"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No final, o código fica estruturado de forma que é possível fazer perguntas e receber respostas contextualizadas com base no conteúdo do arquivo fornecido.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import gradio as gr
import os
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_core.vectorstores import InMemoryVectorStore
from langchain.embeddings import OpenAIEmbeddings
from langchain.chains import RetrievalQA
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv

load_dotenv()  

class HandleLLMUseCase:

    def __init__(self, text_input, file):
        self.text_input = text_input
        self.file = file
        self.docs = None
        self.chunks = None
        self.retriever = None
        self.qa_chain = None

    def handle_pdf_load(self):
        """
        Carrega o PDF enviado e extrai os documentos.
        Se nenhum arquivo for enviado, levanta um erro.
        """
        if self.file is None:
            raise ValueError("Nenhum arquivo enviado.")

        loader = PyPDFLoader(self.file)
        self.docs = loader.load()

    def handle_text_split(self):
        """
        Divide os documentos carregados em chunks menores de 
        500 caracteres com sobreposição de 50 caracteres.
        Se nenhum documento for carregado, levanta um erro.
        """
        if not hasattr(self, 'docs'):
            raise ValueError("Nenhum documento carregado.")

        text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
        self.chunks = text_splitter.split_documents(self.docs)

    def handle_embeddings(self):
        """
        Cria embeddings dos chunks de texto usando OpenAIEmbeddings .
        Se nenhum chunk de texto estiver disponível, levanta um erro.
        """
        if not hasattr(self, 'chunks'):
            raise ValueError("Nenhum chunk de texto disponível.")

        embeddings = OpenAIEmbeddings(openai_api_key=os.environ.get("OPENAI_API_KEY"))
        vectorstore = InMemoryVectorStore.from_documents(self.chunks, embeddings)
        self.retriever = vectorstore.as_retriever()

    def handle_chain_creation(self):
        """
        Cria uma cadeia de perguntas e respostas (RetrievalQA) usando o modelo LLM.
        """
        llm = ChatOpenAI(
            api_key=os.environ.get("OPENAI_API_KEY"),
            organization=os.environ.get("OPENAI_ORGANIZATION_KEY"),
            model="gpt-4o-mini",
        )

        self.qa_chain = RetrievalQA.from_chain_type(
            llm=llm,
            retriever=self.retriever
        )

    def handle_query(self):
        """
        Executa a consulta na cadeia de perguntas e respostas e retorna o resultado.
        """
        response = self.qa_chain.invoke({"query": self.text_input})
        return response["result"]

    def execute(self):
        self.handle_pdf_load()
        self.handle_text_split()
        self.handle_embeddings()
        self.handle_chain_creation()
        response = self.handle_query()
        return response



def handle_file(text_input, file):
    instance = HandleLLMUseCase(text_input, file)
    return instance.execute()


with gr.Blocks() as demo:
    gr.Markdown("## Verificador de PDFs com RAG")
    text_input = gr.Textbox(label="Entre com sua questão aqui", placeholder="Faça uma pergunta sobre o PDF carregado")
    upload = gr.File(type="filepath")
    output = gr.Textbox(label="Output")
    button = gr.Button("Submit")

    button.click(
        fn=handle_file,
        inputs=[text_input, upload],
        outputs=output
    )
demo.launch(share=True)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Referencias
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://python.langchain.com/docs/introduction/" rel="noopener noreferrer"&gt;https://python.langchain.com/docs/introduction/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://datawaybr.medium.com/guia-definitivo-para-vector-databases-bbeeb8f0d802" rel="noopener noreferrer"&gt;https://datawaybr.medium.com/guia-definitivo-para-vector-databases-bbeeb8f0d802&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.gradio.app/" rel="noopener noreferrer"&gt;https://www.gradio.app&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Link do repositório
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/WesleyVitor/rag_pdf_openai_app" rel="noopener noreferrer"&gt;https://github.com/WesleyVitor/rag_pdf_openai_app&lt;/a&gt;&lt;/p&gt;

</description>
      <category>langchain</category>
      <category>rag</category>
      <category>python</category>
      <category>openai</category>
    </item>
    <item>
      <title>Entendendo Relações Genéricas no framework Django</title>
      <dc:creator>Wesley de Morais</dc:creator>
      <pubDate>Sun, 16 Apr 2023 20:47:23 +0000</pubDate>
      <link>https://dev.to/wesleymorais/entendendo-relacoes-genericas-no-framework-django-47ii</link>
      <guid>https://dev.to/wesleymorais/entendendo-relacoes-genericas-no-framework-django-47ii</guid>
      <description>&lt;h2&gt;
  
  
  Sumário
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introdução a relações genéricas&lt;/li&gt;
&lt;li&gt;Estudo de caso com django&lt;/li&gt;
&lt;li&gt;Criando aplicação sem Relação genérica&lt;/li&gt;
&lt;li&gt;Refatorando para ter relação genérica&lt;/li&gt;
&lt;li&gt;Referências&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introdução a relações genéricas
&lt;/h2&gt;

&lt;p&gt;Para entender o que são relações genéricas devemos antes entender algumas associações de banco de dados. Quando temos modelos como Post e Curtida podemos ter uma relação muitos para um, pois um Post pode ter muitas Curtidas, e uma Curtida é de um Post.&lt;/p&gt;

&lt;p&gt;Caso tivermos um modelo Comentário que também pode ter curtida, então não seria de bacana utilizar o modelo Curtida anterior, assim criaríamos um modelo CurtidaComentário, mas podemos entender que ambos os modelos, Curtida e CurtidaComentario, tem o mesmo objetivo, porém para modelos diferentes.&lt;/p&gt;

&lt;p&gt;Assim, podemos usar relação genérica, pois podemos fazer uso do famoso polimorfismo para o modelo Curtida ser tanto usado pelo Post quanto pelo Comentário.&lt;/p&gt;

&lt;h2&gt;
  
  
  Estudo de caso com Django
&lt;/h2&gt;

&lt;p&gt;Vamos utilizar o nosso problema da sessão anterior para entender na prática, então podemos fazer uso de um diagrama entidade relacionamento para clarear a mente.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw7v50hqjhhnqhjmz2oz8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw7v50hqjhhnqhjmz2oz8.png" alt="der" width="710" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Na imagem acima temos, 1 Post tem muitos comentários, 1 comentário pode ter muitas curtidas e  1 Post também pode ter muitas curtidas, porém quando se curte um comentário, este tem relação com um post, só que a instância de curtida de um comentário deve se relacionar com o comentário, mas a curtida de um post não deve se relacionar com um comentário, pois a curtida foi do post, e não do comentário, só que vai ser utilizado o mesmo modelo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Criando aplicação sem Relação genérica
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Dependências
&lt;/h3&gt;

&lt;p&gt;Primeiramente, crie uma ambiente virtual para instalar as dependências da aplicação. Dentro do ambiente virtual instale as seguintes dependências:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir understand_generic_relation 

python -m venv venv

source venv/bin/activate

pip install django
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instalamos o django e django rest framework para fazer a criação da nossa aplicação e dentro da pasta do ambiente virtual digite o comando para criar um projeto e a nossa aplicação:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;django-admin startproject understand_generic_relation .

django-admin startapp core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Settings
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INSTALLED_APPS = [
    ...
    'django.contrib.staticfiles',
    'core.apps.CoreConfig',
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Models
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.db import models
from django.utils import timezone
# Create your models here.

class Post(models.Model):
    text = models.TextField(max_length=400)

class Comment(models.Model):
    post = models.ForeignKey(Post, on_delete=models.DO_NOTHING)
    text = models.TextField(max_length=130)

    def __str__(self) -&amp;gt; str:
        return f"{(self.post.id)}-comment#{self.id}"

class Like(models.Model):
    post = models.ForeignKey(Post, null=True, blank=True, on_delete=models.DO_NOTHING, related_name="likes_post")
    comment = models.ForeignKey(Comment,null=True, blank=True, on_delete=models.DO_NOTHING, related_name="likes_comment")

    created_at = models.DateTimeField(default=timezone.now)

    class Meta:
        # The instance of like is to Post exclusive or Comment
        constraints = [
            models.UniqueConstraint(
                fields=['post'],
                name="unique_like_to_post",
                condition=models.Q(post__isnull=False)
            ),
            models.UniqueConstraint(
                fields=['comment'],
                name="unique_like_to_comment",
                condition=models.Q(comment__isnull=False)
            )
        ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vamos criar 3 modelos, sendo um &lt;strong&gt;&lt;em&gt;Post&lt;/em&gt;&lt;/strong&gt;, um &lt;strong&gt;&lt;em&gt;Comment&lt;/em&gt;&lt;/strong&gt; que tem relação com Post e um &lt;strong&gt;&lt;em&gt;Like&lt;/em&gt;&lt;/strong&gt; que tem relação com ou post ou comentário.&lt;/p&gt;

&lt;p&gt;Crie a migração e execute-as com o seguinte comando no terminal&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py makemigrations
python manage.py migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Shell
&lt;/h3&gt;

&lt;p&gt;Entrando no shell podemos testar a nossa modelagem, entre nele com seguinte comando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py shell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Criando uma postagem e seus comentários:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; from core.models import *
&amp;gt;&amp;gt;&amp;gt; post = Post(text="Atualização do Etherium vai ser boa?")
&amp;gt;&amp;gt;&amp;gt; post.save()
&amp;gt;&amp;gt;&amp;gt; comment1 = Comment(text="Claro que vai!", post=post)
&amp;gt;&amp;gt;&amp;gt; comment1.save()
&amp;gt;&amp;gt;&amp;gt; comment2 = Comment(text="Já deu bom!", post=post)
&amp;gt;&amp;gt;&amp;gt; comment2.save()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Podemos agora criar instâncias de Like para uma postagem ou um comentário&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; like_to_post1 = Like(post=post)
&amp;gt;&amp;gt;&amp;gt; like_to_post1.save()
&amp;gt;&amp;gt;&amp;gt; like_to_comment1 = Like(comment=comment1)
&amp;gt;&amp;gt;&amp;gt; like_to_comment1.save()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quando tentarmos criar algo como “Like(post=post, comment=comment1).save()” receberemos a seguinte mensagem de erro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;django.db.utils.IntegrityError: UNIQUE constraint failed: core_like.post_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assim nosso objetivo foi concluído, mas temos o seguinte o problema. Caso necessitarmos no nosso sistema de mais elementos que podem serem curtidos, o nosso modelo teria muitas chaves estrangeiras nulas para cada instância criada, e também aumentaríamos o tamanho dele.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refatorando para ter relação genérica
&lt;/h2&gt;

&lt;p&gt;Antes de ir para as relações genéricas, vamos entender  o modelo &lt;strong&gt;&lt;em&gt;ContentType,&lt;/em&gt;&lt;/strong&gt; toda vez que um modelo é criado, é criado uma instância de contenttype para aquele modelo, assim este guarda o identificador do modelo criado. Esse modelo vai nos ajudar a saber se uma curtida é de um modelo &lt;strong&gt;&lt;em&gt;Comment&lt;/em&gt;&lt;/strong&gt; ou &lt;strong&gt;&lt;em&gt;Post.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Flush
&lt;/h3&gt;

&lt;p&gt;Vamos apagar os dados no banco com o seguinte comando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py flush
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Models
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.db import models
from django.utils import timezone
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType

# Create your models here.

class Post(models.Model):
    text = models.TextField(max_length=400)
    likes = GenericRelation("Like")

class Comment(models.Model):
    post = models.ForeignKey(Post, on_delete=models.DO_NOTHING)
    text = models.TextField(max_length=130)
    likes = GenericRelation("Like")

    def __str__(self) -&amp;gt; str:
        return f"{(self.post.id)}-comment#{self.id}"

class Like(models.Model):
    content_type = models.ForeignKey(ContentType, on_delete=models.DO_NOTHING, default=None, null=True)
    object_id = models.PositiveIntegerField(default=None, null=True)
    content_object = GenericForeignKey('content_type', 'object_id')

    created_at = models.DateTimeField(default=timezone.now)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Criação do atributo &lt;strong&gt;&lt;em&gt;content_type&lt;/em&gt;&lt;/strong&gt; vai fazer a relação entre os modelos, pois é uma chave estrangeira de &lt;strong&gt;&lt;em&gt;ContentType&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Criação do atributo &lt;strong&gt;&lt;em&gt;object_id&lt;/em&gt;&lt;/strong&gt; vai guardar o valor da instância criada seja de um comentário ou de uma postagem&lt;/li&gt;
&lt;li&gt;Criação do atributo &lt;strong&gt;&lt;em&gt;content_object&lt;/em&gt;&lt;/strong&gt; vai guardar a instância em si, dessa forma quando passarmos a instância para este atributo, então ele vai pegar o id do content_type relacionado e atribuir ao nosso atributo, e também vai pegar o id da instância e atribuir ao nosso atributo.&lt;/li&gt;
&lt;li&gt;Afim de conseguir pegar todas as instâncias de um post, criamos o atributo &lt;strong&gt;&lt;em&gt;likes&lt;/em&gt;&lt;/strong&gt; do tipo &lt;strong&gt;&lt;em&gt;GenericRelation&lt;/em&gt;&lt;/strong&gt; no modelo de &lt;strong&gt;&lt;em&gt;Post&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Afim de conseguir pegar todas as instâncias de um comentário, criamos o atributo &lt;strong&gt;&lt;em&gt;likes&lt;/em&gt;&lt;/strong&gt; do tipo &lt;strong&gt;&lt;em&gt;GenericRelation&lt;/em&gt;&lt;/strong&gt; no modelo de &lt;strong&gt;&lt;em&gt;Comment&lt;/em&gt;&lt;/strong&gt;
### Shell&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Criando uma postagem e seus comentários:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; from core.models import *
&amp;gt;&amp;gt;&amp;gt; post = Post(text="Atualização do Etherium vai ser boa?")
&amp;gt;&amp;gt;&amp;gt; post.save()
&amp;gt;&amp;gt;&amp;gt; comment1 = Comment(text="Claro que vai!", post=post)
&amp;gt;&amp;gt;&amp;gt; comment1.save()
&amp;gt;&amp;gt;&amp;gt; comment2 = Comment(text="Já deu bom!", post=post)
&amp;gt;&amp;gt;&amp;gt; comment2.save()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Podemos agora criar instâncias de Like para uma postagem ou um comentário&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; like_to_post1 = Like(content_object=post)
&amp;gt;&amp;gt;&amp;gt; like_to_post1.save()
&amp;gt;&amp;gt;&amp;gt; like_to_comment1 = Like(content_object=comment1)
&amp;gt;&amp;gt;&amp;gt; like_to_comment1.save()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/WesleyVitor/understand_generic_relation" rel="noopener noreferrer"&gt;Repositório no Github&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://leportella.com/pt-br/relacoes-genericas-django/" rel="noopener noreferrer"&gt;https://leportella.com/pt-br/relacoes-genericas-django/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/pulse/rela%C3%A7%C3%B5es-gen%C3%A9ricas-generic-relation-com-django-fabio-carvalho/?originalSubdomain=pt" rel="noopener noreferrer"&gt;https://www.linkedin.com/pulse/relações-genéricas-generic-relation-com-django-fabio-carvalho/?originalSubdomain=pt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.djangoproject.com/" rel="noopener noreferrer"&gt;https://www.djangoproject.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>django</category>
      <category>database</category>
      <category>mvc</category>
    </item>
    <item>
      <title>Entendendo Task Queue com Django, Celery e RabbitMQ</title>
      <dc:creator>Wesley de Morais</dc:creator>
      <pubDate>Tue, 21 Feb 2023 13:35:56 +0000</pubDate>
      <link>https://dev.to/wesleymorais/entendendo-task-queue-com-django-celery-e-rabbitmq-174b</link>
      <guid>https://dev.to/wesleymorais/entendendo-task-queue-com-django-celery-e-rabbitmq-174b</guid>
      <description>&lt;h2&gt;
  
  
  Sumário
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introdução sobre task queue&lt;/li&gt;
&lt;li&gt;Entendendo o que é celery e rabbitmq&lt;/li&gt;
&lt;li&gt;Estudo de caso com django&lt;/li&gt;
&lt;li&gt;Criando a Api com DRF sem Task Queue&lt;/li&gt;
&lt;li&gt;Usando task queue na API&lt;/li&gt;
&lt;li&gt;Referências&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introdução sobre Task Queue
&lt;/h2&gt;

&lt;p&gt;Quando estamos criando os nossos projetos existem determinadas tarefas que demoram um pouco(Ou muito) para serem processadas, exemplo disso é a geração de relatórios e acesso a determinadas APis que podem estar fora do ar por vários motivos, como acesso a api do governo para checar o CPF de alguém, afim de verificar se o portador tem algum problema legal. Desta forma é imprescindível que o usuário não fique esperando determinadas tarefas serem concluídas para ter um feedback do que aconteceu ou o que poderá acontecer. Portanto, a task queue, ou também chamada de fila de tarefas, é um modo de postergar determinadas tarefas, dessa maneira essas tarefas vão ser feitas de forma assíncronas em relação ao fluxo da aplicação.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvu0f30fw9mtbyhuv8yjk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvu0f30fw9mtbyhuv8yjk.png" alt="taskqueue" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Na imagem acima vemos como funciona esse conceito, assim existe um produtor(Producers) que cria as Tasks para serem processadas, essas vão ser armazenadas dentro de uma fila, dentro do message broker, posteriormente essas tasks vão ser consumidas pelos consumidores(Consumers) para serem processadas, esses podem ou não fazer chamadas ao banco de dados, dependendo da tarefa.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entendendo o que é o Celery e RabbitMQ
&lt;/h2&gt;

&lt;p&gt;O &lt;strong&gt;&lt;em&gt;Celery&lt;/em&gt;&lt;/strong&gt; é uma aplicação python que nos possibilita ter uma abstração para construção de uma &lt;strong&gt;&lt;em&gt;Task Queue&lt;/em&gt;&lt;/strong&gt;, este  tem uma dependência da existência de um &lt;strong&gt;&lt;em&gt;message broker&lt;/em&gt;&lt;/strong&gt; para funcionar. Os consumidores do celery são chamados de &lt;strong&gt;&lt;em&gt;Workers&lt;/em&gt;&lt;/strong&gt; que são processos no qual sempre ficam fazendo solicitações para o broker por novas &lt;strong&gt;&lt;em&gt;tasks&lt;/em&gt;&lt;/strong&gt; para serem processadas. O &lt;strong&gt;&lt;em&gt;RabbitMQ&lt;/em&gt;&lt;/strong&gt; é um dos  &lt;strong&gt;&lt;em&gt;brokers&lt;/em&gt;&lt;/strong&gt; suportados, no qual tem o objetivo de guardar determinadas tasks que serão consumidas pelos workers. Junto com o celery é possível criar múltiplas filas de prioridades, afim de determinadas tarefas serem processadas antes de outras. O protocolo de comunicação entre o produtor e o broker é chamado de AMQP.&lt;/p&gt;

&lt;h2&gt;
  
  
  Estudo de caso com Django
&lt;/h2&gt;

&lt;p&gt;Para entender melhor como funciona a task queue com o celery vamos para uma situação problema, imagine que temos uma aplicação que faz o gerenciamento de uma loja de roupas, no qual podemos gerenciar nossas vendas, um dos endpoints que temos é para gerar relatórios no formato xlsx(Excel) das nossas vendas. Assim podemos ter o seguinte diagrama entidade relacionamento:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4px0wa67tc4md4lxu2zu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4px0wa67tc4md4lxu2zu.png" alt="models" width="707" height="483"&gt;&lt;/a&gt;&lt;br&gt;
Em resumo no nosso diagrama entidade relacionamento acima temos que um vendedor fazendo muitas vendas e uma venda tendo muitas roupas.&lt;/p&gt;
&lt;h2&gt;
  
  
  Criando a Api com DRF sem Task Queue
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Dependências
&lt;/h3&gt;

&lt;p&gt;Primeiramente, crie uma ambiente virtual para instalar as dependências da aplicação. Dentro do ambiente virtual instale as seguintes dependências:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install django djangorestframework model_bakery openpyxl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instalamos o &lt;strong&gt;&lt;em&gt;django&lt;/em&gt;&lt;/strong&gt; e &lt;strong&gt;&lt;em&gt;django rest framework&lt;/em&gt;&lt;/strong&gt; para fazer a criação da nossa aplicação; também instalamos o &lt;strong&gt;&lt;em&gt;model bakery&lt;/em&gt;&lt;/strong&gt; e &lt;strong&gt;&lt;em&gt;openpyxml&lt;/em&gt;&lt;/strong&gt;, a primeira para fazer determinados fakes para a gente fazer o nosso teste, e a segunda para criar o nosso relatório.&lt;/p&gt;

&lt;p&gt;Dentro da pasta do ambiente virtual digite o comando para criar um projeto e a nossa aplicação:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;django-admin startproject store_app .
django-admin startapp core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Models
&lt;/h3&gt;

&lt;p&gt;Vamos mapear o nosso diagrama em modelos&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.db import models
from django.utils import timezone
from django.core.exceptions import ValidationError
# Create your models here.

class Seller(models.Model):
    name = models.CharField(max_length=30)

    def __str__(self) -&amp;gt; str:
        return self.name

class Sale(models.Model):
    total = models.FloatField(default=0)
    created_at = models.DateTimeField(default=timezone.now)

    saller = models.ForeignKey(Seller, on_delete=models.DO_NOTHING)

SIZE_CLOTHES = (
    ('p','P'),
    ('m','M'),
    ('g','G')
)

class Clothe(models.Model):
    name = models.CharField(max_length=50)
    size = models.CharField(max_length=1, choices=SIZE_CLOTHES)
    amount = models.PositiveIntegerField()

    def __str__(self) -&amp;gt; str:
        return f"{self.name}({self.size})"

class ClotheItem(models.Model):
    price_unit = models.FloatField()
    amount_items = models.PositiveIntegerField()

    sale = models.ForeignKey(Sale, on_delete=models.CASCADE, related_name="items", null=False)
    clothe = models.ForeignKey(Clothe, on_delete=models.CASCADE, null=False)
    def clean(self) -&amp;gt; None:
        if self.amount_items &amp;lt; self.clothe.amount:
            raise ValidationError("Amount Item more than Amount Clothes ")
        return super(ClotheItem, self).clean()

    def __str__(self) -&amp;gt; str:
        return f"{self.clothe}"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Criando as migrações e o banco:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py makemigrations
python manage.py migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tasks
&lt;/h3&gt;

&lt;p&gt;O nosso objetivo é criar uma rota que gere um relatório sobre as vendas da nossa loja que pode ser lida pelo Excel, então vamos criar um arquivo na aplicação chamada &lt;strong&gt;&lt;em&gt;tasks.py&lt;/em&gt;&lt;/strong&gt; contendo uma função chamada &lt;strong&gt;&lt;em&gt;create_report_of_sale&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from openpyxl import Workbook
from core.models import Sale

def create_report_of_sale():
    wb = Workbook()
    ws = wb.active
    ws.append(["ID", "Criado Em", "Total","Vendedor","Roupas", "Quantidade de Roupas"])

    sales = Sale.objects.prefetch_related("items")

    for sale in sales:
        date = sale.created_at.strftime('%Y-%m-%d')
        items = []
        for item in sale.items.all():
            items.append(item.clothe.name)

        ws.append([sale.pk,date,sale.total, sale.saller.name, str(items), len(items)])

    wb.save("store_sales.xlsx")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O código acima apenas cria um arquivo store_sales.xlsx contendo uma tabela com alguns dados das vendas.&lt;/p&gt;

&lt;h3&gt;
  
  
  Urls
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.contrib import admin
from django.urls import path
from core.views import CreateReportToSale

urlpatterns = [
    path('admin/', admin.site.urls),
    path('report/sales/', CreateReportToSale.as_view()),
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Views
&lt;/h3&gt;

&lt;p&gt;Vamos criar a nossa view para gerenciar a nossa rota, adicione o seguinte conteúdo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from rest_framework.views import APIView
from core.tasks import create_report_of_sale
from rest_framework.response import Response
import time
# Create your views here.
class CreateReportToSale(APIView):

    def get(self, request):
        tb = time.time()
        create_report_of_sale()
        ta = time.time()
        print(f"Tempo Processando:{ta-tb}s")
        return Response(data={"msg":f"Relatório criado com sucesso !"},status=200)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O código acima cria uma view para gerenciar a rota com o método HTTP GET e criar o nosso relatório. Adicionamos um print para saber o tempo que demora isso.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fazendo acontecer
&lt;/h3&gt;

&lt;p&gt;Para a gente gerar o nosso relatório vamos necessitar de dados, então vamos usar a nossa biblioteca &lt;strong&gt;&lt;em&gt;model_bakery.&lt;/em&gt;&lt;/strong&gt; Abra o shell do django com os seguinte comando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py shell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dentro dele, execute os seguintes comandos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; from model_bakery import baker
&amp;gt;&amp;gt;&amp;gt; baker.make("core.ClotheItem", _quantity=3000)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O código acima vai criar 3000 Itens de roupas, 3000 roupas e 3000 vendas, pois o nosso modelo &lt;strong&gt;&lt;em&gt;ClotheItem&lt;/em&gt;&lt;/strong&gt; está relacionado com esses modelos e temos o argumento &lt;strong&gt;&lt;em&gt;null=False.&lt;/em&gt;&lt;/strong&gt; O código serve para simular a criação de 3000 vendas com 1 item de roupa cada.&lt;/p&gt;

&lt;p&gt;Depois entre na rota da criação do relatório, com o projeto rodando, e vamos ver que existe uma demora para ser processada, e também podemos ver o tempo que demorou quando vamos no terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usando Task Queue na API
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Dependências
&lt;/h3&gt;

&lt;p&gt;Esse é um exemplo de código que demora para ser processado, também temos outros exemplos como acesso a API que esteja fora do ar, envio de email e etc. Assim para resolver o nosso problema vamos utilizar &lt;strong&gt;&lt;em&gt;Task Queue&lt;/em&gt;&lt;/strong&gt;. Primeiramente vamos instalar a nossa biblioteca celery.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install celery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Posteriormente temos que subir o nosso message broker(RabbitMQ) com o seguinte comando docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -d -p 5672:5672 rabbitmq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Celery
&lt;/h3&gt;

&lt;p&gt;Com essas duas dependências podemos dá continuidade no nosso projeto. Comece criando um arquivo chamado &lt;strong&gt;&lt;em&gt;celery.py&lt;/em&gt;&lt;/strong&gt; com o conteúdo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from celery import Celery
import django
import os

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'store_app.settings')
django.setup()
app = Celery("store_app", broker='pyamqp://guest@localhost//')

app.autodiscover_tasks()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O código acima está usando as configurações do arquivo &lt;a href="http://settings.py" rel="noopener noreferrer"&gt;settings.py&lt;/a&gt; e também está criando uma nova instância do celery, dando o nome dela de &lt;strong&gt;&lt;em&gt;store_app&lt;/em&gt;&lt;/strong&gt; e fazendo conexão ao nosso broker pelo protocolo AMQP.&lt;/p&gt;

&lt;p&gt;A linha que se segue é para que todos os apps que foram colocados no &lt;a href="http://settings.py" rel="noopener noreferrer"&gt;settings.py&lt;/a&gt; possam fazer uso dessa instância.&lt;/p&gt;

&lt;p&gt;Uma outra configuração que devemos fazer é dentro do arquivo “&lt;strong&gt;init&lt;/strong&gt;.py” do projeto, afim de inicializar a instância do celery quando o django inicializar o projeto, e assim as nossas aplicações poderão fazer acesso a partir da função &lt;strong&gt;&lt;em&gt;shared_task&lt;/em&gt;&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from celery import app

__all__ = ('app',)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tasks
&lt;/h3&gt;

&lt;p&gt;No arquivo &lt;strong&gt;&lt;em&gt;tasks.py&lt;/em&gt;&lt;/strong&gt; podemos modificar o código para o seguinte:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from openpyxl import Workbook
from core.models import Sale
from celery import shared_task #&amp;lt;-new
@shared_task(
        bind=True,
        max_retries=5,
        default_retry_delay=30) #&amp;lt;-new
def create_report_of_sale(self): #&amp;lt;-new
    try: #&amp;lt;-new
        wb = Workbook()
        ws = wb.active
        ws.append(["ID", "Criado Em", "Total","Vendedor","Roupas", "Quantidade de Roupas"])

        sales = Sale.objects.prefetch_related("items")

        for sale in sales:
            date = sale.created_at.strftime('%Y-%m-%d')
            items = []
            for item in sale.items.all():
                items.append(item.clothe.name)

            ws.append([sale.pk,date,sale.total, sale.saller.name, str(items), len(items)])

        wb.save("sample.xlsx")
    except: #&amp;lt;-new
        raise self.retry() #&amp;lt;-new
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No conteúdo acima estamos decorando a nossa função usando o decorator &lt;strong&gt;&lt;em&gt;shared_task,&lt;/em&gt;&lt;/strong&gt; usando este decorator estamos criando uma nova task que poderá ser chamada por qualquer código, e ainda estamos passando alguns argumentos para ele, como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;bind&lt;/em&gt;&lt;/strong&gt; - Nos possibilita acessar determinadas propriedades da task específica, como o objeto de requisição ou a função retry, que nos possibilita executar novamente a task caso algo tenha dado errado;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;max_retries&lt;/em&gt;&lt;/strong&gt;  - É o contador que adiciona um número máximo de tentativas de chamada da função retry;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;default_retry_delay&lt;/em&gt;&lt;/strong&gt; - É o tempo entre uma chamada ao retry e outra.
### View&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Na nossa view podemos modificar o código para o seguinte:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from rest_framework.views import APIView
from core.tasks import create_report_of_sale
from rest_framework.response import Response
import time
# Create your views here.
class CreateReportToSale(APIView):

    def get(self, request):
        tb = time.time()
        create_report_of_sale.delay() #&amp;lt;-new
        ta = time.time()
        print(f"Tempo de Processamento:{ta-tb}")
        return Response(data={"msg":f"Relatório criado com sucesso !"},status=200)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com essa simples chamada a “mágica” pode acontecer, mas para isso é importante criarmos nossos consumidores de tasks, então crie um outro terminal e adicione o seguinte código:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;celery -A store_app worker --loglevel=INFO
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Obs.: Lembre que o nosso process worker não tem hot reloading, ou seja, todas as vezes que modificar sua task rode o código acima.&lt;/p&gt;

&lt;p&gt;Entre no seu projeto e acesse a rota de criação do nosso relatório. Como podemos ver as coisas ocorrem bem mais rápido, pois quando acessamos a nossa rota criamos uma task que foi adicionada ao broker e consumida por um worker disponível, e o tempo de adicionar um task no broker é bem mais rápida do que processa-la.&lt;/p&gt;

&lt;p&gt;Repositório: &lt;br&gt;
&lt;a href="https://github.com/WesleyVitor/task_queue_django/tree/main" rel="noopener noreferrer"&gt;Código Desenvolvido&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Referências
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.celeryq.dev/en/stable/index.html" rel="noopener noreferrer"&gt;https://docs.celeryq.dev/en/stable/index.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://denibertovic.com/posts/celery-best-practices/" rel="noopener noreferrer"&gt;https://denibertovic.com/posts/celery-best-practices/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.django-rest-framework.org/" rel="noopener noreferrer"&gt;https://www.django-rest-framework.org&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.djangoproject.com/en/4.1/" rel="noopener noreferrer"&gt;https://docs.djangoproject.com/en/4.1/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>website</category>
    </item>
    <item>
      <title>Entendendo o problema de N+1 query usando o Django</title>
      <dc:creator>Wesley de Morais</dc:creator>
      <pubDate>Mon, 13 Feb 2023 01:44:54 +0000</pubDate>
      <link>https://dev.to/wesleymorais/entendendo-o-problema-de-n1-query-usando-o-orm-do-django-4f43</link>
      <guid>https://dev.to/wesleymorais/entendendo-o-problema-de-n1-query-usando-o-orm-do-django-4f43</guid>
      <description>&lt;h2&gt;
  
  
  Sumário
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introdução do que é o problema n+1 query&lt;/li&gt;
&lt;li&gt;Entendendo a situação problema em Django&lt;/li&gt;
&lt;li&gt;Criação da API com DRF&lt;/li&gt;
&lt;li&gt;Resolvendo usando o que o Django dispõe por padrão&lt;/li&gt;
&lt;li&gt;Entendendo o Django Virtual Model&lt;/li&gt;
&lt;li&gt;Referências&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introdução do que é o problema n+1 query
&lt;/h2&gt;

&lt;p&gt;O problema N+1 query é um problema que ocorre quando existem determinadas associações entre entidades na aplicação, desta forma quando é necessário acessar informações de A em relação a B ocorre muitas requisições ao banco de dados para cada entidade de A, tornando a aplicação lenta e possivelmente gastando mais do que o necessário quando a aplicação está em produção.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entendendo a situação problema em Django
&lt;/h2&gt;

&lt;p&gt;Para podermos entender o problema de n+1 query, vamos a um exemplo, imagine uma aplicação na qual usuários de uma plataforma postam vídeos, e esses vídeos tem determinadas categorias que o próprio usuário pode adicionar para que o vídeo chegue em pessoas que gostam dessas categorias. Desta forma podemos ter o seguinte diagrama entidade relacionamento:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh5mykh2gmv5gr4vtiafo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh5mykh2gmv5gr4vtiafo.png" alt="der" width="711" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Podemos pensar um pouco em relação ao primeiro relacionamento, um usuário publica muitos vídeos, e um vídeo é de um usuário, temos assim um relacionamento 1 para muitos. Imagine que temos 3 usuários na plataforma inteira(Começou agora), e cada um postou 1 vídeo(Uma máquina de postagem), e temos uma rota na nossa API que nos mostra informações dos usuários e o título do vídeo que ele postou, então vai ser necessário 1 query para pegar todos os Users e N queries para pegar as informações dos vídeos relacionados a cada User.  Assim temos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT * FROM User ...
SELECT title FROM Video WHERE userID = 1
SELECT title FROM Video WHERE userID = 2
SELECT title FROM Video WHERE userID = 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O grande problema ocorre quando aumentamos o número de usuários. Em relação a segunda associação as ideias são as mesmas. Então já podemos imaginar qual a solução para problema, eu vou precisar fazer uma query para pegar todos os usuários e uma outra para pegar os vídeos de todos os usuários&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT * FROM User ...
SELECT title FROM Video WHERE userID in (1,2,3)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mas surge a pergunta, como faço isso usando o ORM do django?&lt;/p&gt;

&lt;h2&gt;
  
  
  Criação da API com DRF
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pré-requisitos
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Entendimento básico sobre o funcionamento do Django&lt;/li&gt;
&lt;li&gt;Entendimento básico sobre o funcionamento do Django Rest Framework&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Mão na massa
&lt;/h3&gt;

&lt;p&gt;Crie uma pasta chamada &lt;strong&gt;video_plataform&lt;/strong&gt; e instale um ambiente virtual para não poluir seu ambiente.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python -m venv venv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instalando dependências:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install django djangorestframework
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Criar o projeto e a aplicação:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;django-admin startproject video_plataform .
django-admin startapp core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Settings
&lt;/h3&gt;

&lt;p&gt;Posteriormente, vamos adicionar no arquivo settings.py as duas aplicações&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    "core.apps.CoreConfig", #&amp;lt;- new
    "rest_framework" #&amp;lt;- new
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Models
&lt;/h3&gt;

&lt;p&gt;Para fazer o mapeamento podemos adicionar o seguinte código no arquivo models.py  e fazer a migração ao banco de dados:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.db import models


class User(models.Model):
    name = models.CharField(max_length=30)

    def __str__(self) -&amp;gt; str:
        return self.name

class Video(models.Model):
    title = models.CharField(max_length=30)
    url = models.CharField(max_length=255)
    visualizations = models.IntegerField(default=0)
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="videos")

    def __str__(self) -&amp;gt; str:
        return self.title

class Category(models.Model):
    title = models.CharField(max_length=30)
    videos = models.ManyToManyField(Video, related_name="categories")

    def __str__(self) -&amp;gt; str:
        return self.title
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py makemigrations
python manage.py migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Povoando o banco de dados
&lt;/h3&gt;

&lt;p&gt;Crie um diretório chamado fixtures  e dentro dele crie um arquivo chamado &lt;strong&gt;&lt;em&gt;data.json&lt;/em&gt;&lt;/strong&gt;  com o seguinte conteúdo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[   {
    "model": "core.user",
    "pk": 1,
    "fields": {
        "name": "Paul"
        }
    },{
        "model": "core.video",
        "pk": 1,
        "fields": {
            "title": "Dancinha do loirinho",
            "url":"www.google.com.br",
            "visualizations": 0,
            "user": 1
        }
    },{
    "model": "core.user",
    "pk": 2,
    "fields": {
        "name": "Marie"
        }
    },{
        "model": "core.video",
        "pk": 2,
        "fields": {
            "title": "Dancinha do forró",
            "url":"www.google.com.br",
            "visualizations": 0,
            "user": 2
        }
    },{
    "model": "core.user",
    "pk": 3,
    "fields": {
        "name": "Mary"
        }
    },{
        "model": "core.video",
        "pk": 3,
        "fields": {
            "title": "The office",
            "url":"www.google.com.br",
            "visualizations": 0,
            "user": 3
        }
    },{
    "model": "core.category",
    "pk": 1,
    "fields": {
        "title": "Entretenimento",
        "videos":[1,2,3]
        }
    },{
    "model": "core.category",
    "pk": 2,
    "fields": {
        "title": "Forró",
        "videos":[2]
        }
    },{
    "model": "core.category",
    "pk": 3,
    "fields": {
        "title": "Funk",
        "videos":[1]
        }}]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No terminal execute o comando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py loaddata data.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Serializer
&lt;/h3&gt;

&lt;p&gt;Crie o arquivo &lt;strong&gt;&lt;a href="http://serializers.py" rel="noopener noreferrer"&gt;serializers.py&lt;/a&gt;&lt;/strong&gt; e coloque o conteúdo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from rest_framework import serializers
from core.models import User, Video, Category

class VideoSerializer(serializers.ModelSerializer):

    class Meta:
        model = Video
        fields = ("title","url","visualizations")
class UserSerializer(serializers.ModelSerializer):
    videos = VideoSerializer(many=True)
    class Meta:
        model = User
        fields = ("name", "videos")

class CategorySerializer(serializers.ModelSerializer):
    videos = VideoSerializer(many=True)

    class Meta:
        model = Category
        fields = ("title")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  URL
&lt;/h3&gt;

&lt;p&gt;No arquivo &lt;a href="http://urls.py" rel="noopener noreferrer"&gt;urls.py&lt;/a&gt; na pasta do projeto adicione o seguinte comando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.contrib import admin
from django.urls import path
from core.views import UserListAPIView #&amp;lt;- new

urlpatterns = [
    path('admin/', admin.site.urls),
    path('users/', UserListAPIView.as_view()) #&amp;lt;- new
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  View
&lt;/h3&gt;

&lt;p&gt;Afim de podermos fazer a listagem de usuários da nossa aplicação vamos criar a nossa view &lt;strong&gt;&lt;em&gt;UserListAPIView&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from rest_framework.generics import ListAPIView
from core.serializers import UserSerializer
from core.models import User
from django.db import connection, reset_queries
class UserListAPIView(ListAPIView):
    serializer_class = UserSerializer
    def get_queryset(self):
        users = User.objects.all()

        for query in connection.queries:
            print("Query:", query)

        reset_queries()

        return users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O código é um pouco curioso, temos uma classe que herda de ListAPIView, pois eu quero que ela faça a funcionalidade de listagem de todos as instâncias de um determinado recurso de uma forma serializada, assim eu faço o comando User.objects.all(), mas o django nos possibilita saber quais foram as queries feitas ao banco usando o atributo connection.queries,  assim caso você acesse a rota pelo browser e for ao seu terminal vai encontra algo semelhante a isso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Query: {'sql': 'SELECT "core_user"."id", "core_user"."name" FROM "core_user"', 'time': '0.004'}
Query: {'sql': 'SELECT "core_video"."id", "core_video"."title", "core_video"."url", "core_video"."visualizations", "core_video"."user_id" FROM "core_video" WHERE "core_video"."user_id" = 1', 'time': '0.003'}
Query: {'sql': 'SELECT "core_video"."id", "core_video"."title", "core_video"."url", "core_video"."visualizations", "core_video"."user_id" FROM "core_video" WHERE "core_video"."user_id" = 2', 'time': '0.000'}
Query: {'sql': 'SELECT "core_video"."id", "core_video"."title", "core_video"."url", "core_video"."visualizations", "core_video"."user_id" FROM "core_video" WHERE "core_video"."user_id" = 3', 'time': '0.000'}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se você lembrar são, praticamente, as mesmas queries que mostrei anteriormente, ou seja, podemos ver o problema aqui, caso o número de usuários aumente, então maior vai ser o número de queries feitas para pegar os vídeos deles para essa view.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resolvendo usando o que o Django dispõe por padrão
&lt;/h2&gt;

&lt;p&gt;Para resolver esse problema vamos modificar o modo como usamos o ORM, assim modifique:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#users = User.objects.all()
#=&amp;gt;
users = User.objects.prefetch_related("videos")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Decorrente que estamos fazer um acesso inverso, pois quem tem a chave estrangeira na relação é o modelo Video e não User, temos que usar o a função prefetch_related()  que faz 2 queries e é feito um join por meio do python, também temos o select_related() que faz a mesma coisa, mas deve ser usado em modelos que tenha a chave estrangeira e também ocorre o join por meio do banco de dados, assim fazendo apenas 1 query. Portanto, se acessarmos a rota e formos no terminal vamos ver algo parecido com:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Query: {'sql': 'SELECT "core_user"."id", "core_user"."name" FROM "core_user"', 'time': '0.004'}
Query: {'sql': 'SELECT "core_video"."id", "core_video"."title", "core_video"."url", "core_video"."visualizations", "core_video"."user_id" FROM "core_video" WHERE "core_video"."user_id" IN (1, 2, 3)', 'time': '0.003'}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como podemos ver na query, mesmo que o sistema tenha 1 milhão de usuários, apenas será feita 2 requisições ao banco de dados, e não 1 milhão de requisições mais 1.&lt;/p&gt;

&lt;h3&gt;
  
  
  Indo Além
&lt;/h3&gt;

&lt;p&gt;Vamos adicionar um dado novo aos dados do usuário que são mostrados, podemos querer mostrar a quantidade de &lt;strong&gt;&lt;em&gt;Vídeos&lt;/em&gt;&lt;/strong&gt; que cada usuário tem, podemos adicionar o seguinte código no arquivo &lt;strong&gt;&lt;em&gt;serializer.py:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class UserSerializer(serializers.ModelSerializer):
    videos = VideoSerializer(many=True)
    amount_videos = serializers.SerializerMethodField() #&amp;lt;-new
    class Meta:
        model = User
        fields = ("name", "videos", "amount_videos")   #&amp;lt;-new

    def get_amount_videos(self, obj):                  #&amp;lt;-new
        return Video.objects.filter(user=obj).count()  #&amp;lt;-new
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O código acima vai criar uma nova chave nos dados que são mandados para o cliente com a quantidade de vídeos de cada usuário, porém podemos visualizar as queries feitas pelo terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Query: {'sql': 'SELECT "core_user"."id", "core_user"."name" FROM "core_user"', 'time': '0.002'}
Query: {'sql': 'SELECT "core_video"."id", "core_video"."title", "core_video"."url", "core_video"."visualizations", "core_video"."user_id" FROM "core_video" WHERE "core_video"."user_id" IN (1, 2, 3)', 'time': '0.001'}

Query: {'sql': 'SELECT COUNT(*) AS "__count" FROM "core_video" WHERE "core_video"."user_id" = 1', 'time': '0.001'}
Query: {'sql': 'SELECT COUNT(*) AS "__count" FROM "core_video" WHERE "core_video"."user_id" = 2', 'time': '0.000'}
Query: {'sql': 'SELECT COUNT(*) AS "__count" FROM "core_video" WHERE "core_video"."user_id" = 3', 'time': '0.000'}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Veja, as nossas duas queries ainda estão lá(TUDO OK!), evitamos o problema de n+1 query, mas para saber a quantidade de vídeos de cada usuário o problema ainda persiste.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entendendo o Django Virtual Model
&lt;/h2&gt;

&lt;p&gt;Para podermos resolver o problema proposto, uma das formar é usar uma biblioteca chamada django virtual model, esta biblioteca nos oferece um meio de resolver o problema de N+1 query, mas ainda criar um código com uma alta &lt;strong&gt;mantenabilidade&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instale a biblioteca:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install django-virtual-model
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Crie um arquivo chamado virtual_models.py na aplicação com o seguinte código:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import django_virtual_models as v
from core.models import User, Video
from django.db.models import Count
class VirtualVideo(v.VirtualModel):
    class Meta:
        model = Video
class VirtualUser(v.VirtualModel):
    videos = VirtualVideo(manager=Video.objects)

    amount_videos = v.Annotation(
        lambda qs, **kwargs: qs.annotate(
            amount_videos=Count("videos")
        ).distinct()
    )
    class Meta:
        model = User
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O código acima vai criar modelos virtuais que nos auxiliaram na processo de pré-processamento tanto dos vídeos quanto da quantidade deles, podemos ver que estamos usando uma anotação afim de adicionar no atributo amount_videos a quantidade de vídeos de um usuário.&lt;/p&gt;

&lt;p&gt;No nosso arquivo serializers.py podemos modificar o serializer de usuário para o seguinte:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import django_virtual_models as dvm
class UserSerializer(dvm.VirtualModelSerializer):
    videos = VideoSerializer(many=True)
    amount_videos = serializers.IntegerField(read_only=True)

    class Meta:
        model = User
        virtual_model = VirtualUser
        fields = ("name", "videos","amount_videos")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E a nossa view ficará a seguinte:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class UserListAPIView(dvm.VirtualModelListAPIView):
    serializer_class = UserSerializer
    queryset = User.objects.all()

    def get_queryset(self):

        queryset = super().get_queryset()
        for query in connection.queries:
            print("Query:", query)
        reset_queries()
        return queryset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No código acima estamos já usando um queryset geral, pois a nossa biblioteca já cuida de resolver o nosso problema de vídeos e também da quantidade de vídeos. Assim, quando visitarmos a rota e visualizar o terminal podemos ver algo como:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Query: {'sql': 'SELECT DISTINCT "core_user"."id", "core_user"."name", COUNT("core_video"."id") AS "amount_videos" FROM "core_user" LEFT OUTER JOIN "core_video" ON ("core_user"."id" = "core_video"."user_id") GROUP BY "core_user"."id", "core_user"."name"', 'time': '0.001'}
Query: {'sql': 'SELECT "core_video"."id", "core_video"."title", "core_video"."url", "core_video"."visualizations", "core_video"."user_id" FROM "core_video" WHERE "core_video"."user_id" IN (1, 2, 3)', 'time': '0.000'}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Podemos ver que voltamos para a quantidade de 2 queries, pois na query de pegar os dados já é feita a contagem dos vídeos. Legal em!?&lt;/p&gt;

&lt;p&gt;Código desenvolvido: &lt;a href="https://github.com/WesleyVitor/django_query_problem" rel="noopener noreferrer"&gt;Repositório&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://vintasoftware.github.io/django-virtual-models/" rel="noopener noreferrer"&gt;Django Virtual Model&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.django-rest-framework.org" rel="noopener noreferrer"&gt;Django Rest Framework&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.djangoproject.com/en/4.1/ref/models/querysets/#prefetch-related" rel="noopener noreferrer"&gt;Django official&lt;/a&gt;&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Elixir - Como fazer determinadas operações com a biblioteca Ecto (Parte 2)</title>
      <dc:creator>Wesley de Morais</dc:creator>
      <pubDate>Fri, 27 Jan 2023 13:54:33 +0000</pubDate>
      <link>https://dev.to/wesleymorais/elixir-como-fazer-determinadas-operacoes-com-a-biblioteca-ecto-parte-2-2gbh</link>
      <guid>https://dev.to/wesleymorais/elixir-como-fazer-determinadas-operacoes-com-a-biblioteca-ecto-parte-2-2gbh</guid>
      <description>&lt;p&gt;Vamos para segunda parte do nosso artigo, agora vamos as operações propriamente ditas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fazendo Operações no banco de dados
&lt;/h2&gt;

&lt;p&gt;Todas as operações que iremos fazer será usando o shell que o elixir nos oferece, então digite no terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex -S mix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Operações sem usar Ecto.Schema
&lt;/h2&gt;

&lt;p&gt;Algumas vezes será necessário fazer operações de banco de dados sem a utilização do Ecto.Schema, e apenas usar as tabelas associadas, porém mesmo sem a utilização do Ecto.Schema não vamos necessitar criar o SQL bruto.&lt;/p&gt;

&lt;h3&gt;
  
  
  Busca
&lt;/h3&gt;

&lt;p&gt;A primeira operação que vamos fazer será a de busca, com o seguinte comando podemos criar uma query para verificar todos os clientes inseridos no nosso banco de dados:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Ecto.Query
alias PizzariaEcto.Repo
query = from("clients", select: [:name, :phone])
Repo.all(query)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O código acima vai criar o seguinte sql e executa-lo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT name, phone FROM clients
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como eu falei a um tempo quando criamos a query ela não será executada, pois o Ecto usa o padrão repositório, ou seja, apenas funções do repositório poderá operar em cima do banco de dados. Assim, essa função Repo.all() irá receber uma query(Uma struct Ecto.Query) e retornar uma lista de mapas contendo as informações no banco, mas como não temos nada no banco será retornado uma lista vazia, então vamos inserir algo no banco de dados.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inserção
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data = [
    %{name: "Bruno Santos", phone: "999878767"},
  %{name: "Maria Pereira", phone: "994657876"},
  %{name: "Fábio Bio", phone: "999870900"}
]
Repo.insert_all("clients", data)
#=&amp;gt; {3, nil}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com esse código inserirmos 3 clientes na nossa base de dados, então podemos novamente executar o comando de busca para ver esses clientes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Busca específica
&lt;/h3&gt;

&lt;p&gt;Afim de poder executar uma operação de busca específica podemos executar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Ecto.Query
alias PizzariaEcto.Repo
query = from(c in "clients", where: c.name == "Bruno Santos", select: [:name, :phone])
Repo.all(query)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Remoção
&lt;/h3&gt;

&lt;p&gt;Afim de poder executar uma operação de remoção de dados podemos executar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Ecto.Query
alias PizzariaEcto.Repo
from(c in "clients", where: c.name == "Bruno Santos") |&amp;gt; Repo.delete_all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Atualização
&lt;/h3&gt;

&lt;p&gt;Podemos fazer atualizações também ainda sem usar schemas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Ecto.Query
alias PizzariaEcto.Repo
from(c in "clients", where: c.name == "Fábio Bio") |&amp;gt; Repo.update_all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Operações usando o Ecto.Schema
&lt;/h2&gt;

&lt;p&gt;Com os schemas em mãos podemos fazer a mesma coisa que fazermos sem os schemas, porém o retorno dessas operações vão está em estruturas elixir.&lt;/p&gt;

&lt;h3&gt;
  
  
  Busca
&lt;/h3&gt;

&lt;p&gt;As buscas usando Ecto.Schema é a mesma coisa de sem usa essa estrutura, mas o retorno da função será uma lista de estruturas elixir populadas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Ecto.Query
alias PizzariaEcto.{Repo, Client}
query = from(Client)
Repo.all(query)
#=&amp;gt; [%PizzariaEcto.Client{...},...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Buscas específicas
&lt;/h3&gt;

&lt;p&gt;Podemos fazer retornos em estruturas elixir usando a função Repo.get_by().&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Ecto.Query
alias PizzariaEcto.{Repo, Client}

Repo.get_by(Client, name: "Maria Pereira")
#=&amp;gt; %PizzariaEcto.Client{...}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Ecto.Changeset
&lt;/h2&gt;

&lt;p&gt;Antes de fazermos operações com inserção e atualização de dados usando as estruturas elixir, podemos falar sobre a estrutura Ecto.Changeset. Esta estrutura nos permite criar casts e validações para dados que são inseridos pelo usuário.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alias PizzariaEcto.Client
import Ecto.Changeset
params = %{name: "Mariana Bom", phone: "999808909", email: "mariana@gmail.com"}
changeset = cast(Client, params, [:name, :phone])
Repo.insert(changeset)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A função cast permiti filtrar quais chaves vão poder serem usadas para criação da estrutura PizzariaEcto.Client, e assim usando a função insert inserir no banco de dados. O ecto tem funções de validação como o validate_length que recebe um changeset e retorna um novo changeset com essa validação aplicada, então podemos melhorar o nosso código:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alias PizzariaEcto.Client
import Ecto.Changeset
params = %{name: "Mariana Bom", phone: "999808909", email: "mariana@gmail.com"}
changeset = cast(%Client{}, params, [:name, :phone]) |&amp;gt; validate_length(:phone, min: 9)
Repo.insert(changeset)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O código acima permite inserir apenas os dados de &lt;strong&gt;name&lt;/strong&gt; e &lt;strong&gt;phone&lt;/strong&gt; que tenha no mínimo 9 dígitos, caso alguma validação não ocorra a inserção não funcionará. &lt;/p&gt;

&lt;p&gt;Podemos fazer atualizações usando &lt;strong&gt;changeset&lt;/strong&gt;, o seguinte código vai procurar um cliente chamado “Maria Bom”, criar um &lt;strong&gt;changeset&lt;/strong&gt; com base nesse retorno e mandar para a função &lt;strong&gt;update&lt;/strong&gt; para atualizar o banco.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alias PizzariaEcto.{Repo,Client}
import Ecto.Changeset
client = Repo.get_by(Client, name: "Mariana Bom")
changeset = change(client, name: "Mariana Ruim")
Repo.update(changeset)
#=&amp;gt; {:ok, struct_com_cliente_atualizado}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;No presente artigo vimos como a ferramenta Ecto nos possibilita fazer operações no banco de dados de nossa preferência. O uso do Ecto também se dá principalmente junto ao framework Phoenix que nos permite criar aplicações web poderosas, afim de resolver alguns problemas existentes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/ecto/Ecto.html" rel="noopener noreferrer"&gt;Ecto Site Oficial&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pragprog.com/titles/wmecto/programming-ecto/" rel="noopener noreferrer"&gt;Programming Ecto - Build Database Apps in Elixir for Scalability and Performance&lt;/a&gt;&lt;/p&gt;

</description>
      <category>announcement</category>
      <category>productivity</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Elixir - Como fazer determinadas operações com a biblioteca Ecto (Parte 1)</title>
      <dc:creator>Wesley de Morais</dc:creator>
      <pubDate>Fri, 27 Jan 2023 13:54:11 +0000</pubDate>
      <link>https://dev.to/wesleymorais/elixir-como-fazer-determinadas-operacoes-com-a-biblioteca-ecto-parte-1-17le</link>
      <guid>https://dev.to/wesleymorais/elixir-como-fazer-determinadas-operacoes-com-a-biblioteca-ecto-parte-1-17le</guid>
      <description>&lt;h2&gt;
  
  
  Requisitos
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Elixir 1.14.2&lt;/li&gt;
&lt;li&gt;Erlang/otp 25&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introdução
&lt;/h2&gt;

&lt;p&gt;O Ecto é uma biblioteca feita em elixir que nos possibilita ter uma Api, assim como uma abstração para fazer operações em diferentes tipos de bancos de dados. Por meio dele podemos fazer uso de drives de banco de dados como &lt;strong&gt;Mysql(Myxql)&lt;/strong&gt; e &lt;strong&gt;Postgresql(Postgrex).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Esta biblioteca se utiliza de um padrão chamado &lt;strong&gt;Repository&lt;/strong&gt;, no qual todo e qualquer acesso ao banco de dados se dará por meio de funções de um determinado repositório criado. O Ecto é dividido em 4 componentes principais:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ecto.Repo

&lt;ul&gt;
&lt;li&gt;Responsável por ser o Repositório de acesso ao banco, ou seja, operações de busca(Query), Inserção, atualização e remoção de dados do banco de dados será por meio de funções dele.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Ecto.Query

&lt;ul&gt;
&lt;li&gt;Responsável por ter funções de criação de Query, ou seja, ler do banco de dados alguma informação.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Ecto.Schema

&lt;ul&gt;
&lt;li&gt;Responsável por mapear determinados dados externos para estruturas elixir.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Ecto.Changeset

&lt;ul&gt;
&lt;li&gt;Responsável por validar e filtrar determinados dados recebidos, afim de acessar o banco de dados.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Situação problema
&lt;/h2&gt;

&lt;p&gt;Nada melhor do que entender uma nova tecnologia do que em uma situação problema que você vai aplica-la. &lt;/p&gt;

&lt;p&gt;Imagine que um determinado proprietário de uma pizzaria tenha apenas o interesse de manter os dados dos clientes, assim como as pizzas que ele poderá fazer e os pedidos desses clientes para determinadas pizzas existentes.&lt;/p&gt;

&lt;p&gt;Podemos mapear um modelo relacional dessa maneira:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9fsf7suqgu1qiiumsdg0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9fsf7suqgu1qiiumsdg0.png" alt="mapeamento" width="704" height="563"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Mão na massa
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Criando o projeto
&lt;/h2&gt;

&lt;p&gt;Primeiramente vamos criar o nosso projeto usando a ferramenta Mix, então digite no terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix new pizzaria_ecto --sup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caso você nunca criou um projeto com o mix, aqui estamos apenas gerando uma estrutura inicial e adicionando um supervisor na nosso projeto, precisamos desse supervisor, pois a nossa aplicação com ecto será um processo e caso ocorra algum erro nele, por alguma razão, então o nosso supervisor irá reiniciar o nosso processo do inicio(let it crash 🥳).&lt;/p&gt;

&lt;h2&gt;
  
  
  Instalando as dependências
&lt;/h2&gt;

&lt;p&gt;Antes de a gente trabalharmos com o ecto temos que ter essa biblioteca no nosso projeto, então vamos adicionar essas duas dependências na função deps dentro do arquivo mix.exs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defp deps do
    [
      {:postgrex, "~&amp;gt; 0.16.5"},
      {:ecto_sql, "~&amp;gt; 3.9"}
    ]
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui estamos usando o drive do postgresql e estamos fazendo uso de uma biblioteca ecto_sql que tem como dependência o ecto, mas adiciona outras funcionalidades para trabalharmos com banco de dados relacionais. Para instalar e compilar as aplicações devemos rodar o seguinte comando no terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix do deps.get, compile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Criando o nosso próprio repositório
&lt;/h3&gt;

&lt;p&gt;Dentro do diretório pizzaria_ecto que foi criado poderemos criar um arquivo chamado repo.ex, com seguinte código:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defmodule PizzariaEcto.Repo do
  use Ecto.Repo, otp_app: :pizzaria_ecto, adapter: Ecto.Adapters.Postgres
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui criamos um módulo que chamamos de PizzariaEcto.Repo no qual ele faz uso do Ecto.Repo, eu gosto de pensar que quando usamos o “use” fazemos com que o nosso módulo seja um módulo Ecto.Repo . Além disso temos a keyword :otp_app que nos diz quais configurações vamos usar dentro de um arquivo chamado config.exs(Vamos criar ainda), e por último termos a keyword :adapter que dizemos para o ecto que vamos usar o banco de dados Postgresql. Agora vamos criar um diretório chamado config(Na raiz do projeto) e dentro dele vamos criar um arquivo config.exs com seguinte código:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Mix.Config


config :pizzaria_ecto, ecto_repos: [PizzariaEcto.Repo]

config :pizzaria_ecto, PizzariaEcto.Repo,
  database: "pizzaria_ecto",
  username: "postgres", # Username "postgres" is stardard user in pgdb
  password: "postgres", # Password "postgres" is our password when created pg container
  hostname: "localhost" # We expose our port when created our pg container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Na código acima estamos fazendo algumas configurações para o nosso querido ecto, no qual temos o uso da macro config que setamos qual o nome da configuração, veja que usamos o mesmo atom usado na keyword &lt;em&gt;:opt_app&lt;/em&gt; no módulo &lt;strong&gt;PizzariaEcto.Repo&lt;/strong&gt;, além disso temos uma keyword dizendo quais serão os repositórios no nosso projeto, poderiamos ter mais de um acessando diferentes banco de dados.&lt;/p&gt;

&lt;p&gt;Na segunda chamada a macro &lt;strong&gt;config&lt;/strong&gt; temos as informações necessárias para o adapter acessar o banco de dados rodando. Opps, ainda não subimos um container postgresql, então vamos fazer isso.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subindo container postgresql
&lt;/h2&gt;

&lt;p&gt;No diretório home do projeto vamos criar um arquivo docker-compose.yml com seguinte código:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3.9'

services:
  db:
    container_name: "pg_pizzaria_ecto"
    image: postgres:latest
    environment:
      POSTGRES_PASSWORD: postgres
    volumes:
      - postgres-db:/var/lib/postgresql/data
    ports:
      - '5432:5432'

volumes:
  postgres-db:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Posteriormente vamos subir o container com seguinte comando no terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Criando Schemas
&lt;/h3&gt;

&lt;p&gt;Uma das formas de mapear determinados dados externos para estruturas elixir é usando o Ecto.Schema, com elas é possível fazer operações no banco de dados com retornos em estruturas elixir específicas. Dentro do diretório pizzaria_ecto crie um arquivo chamado client.ex com o seguinte conteúdo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defmodule PizzariaEcto.Client do
  use Ecto.Schema
  alias PizzariaEcto.{Order}
  schema "clients" do
    field :name, :string
    field :phone, :string
      timestamps() # field :updated_at, :naive_datetime
                                 # field :inserted_at, :naive_datetime 

    has_many :order, Order
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui temos um módulo que é um Ecto.Schema, assim algumas funções e macros podem ser chamadas dentro do módulo que ele é especificado, um destes macros é a schema que recebe como argumento a tabela no banco de dados, assim ele vai mapear determinados campos para uma tabela no banco de dados, porém ainda não será criada as tabelas no banco de dados.&lt;/p&gt;

&lt;h2&gt;
  
  
  Associação Um para Muitos
&lt;/h2&gt;

&lt;p&gt;Algo curioso que podemos ver no schema de PizzariaEcto.Client é a macro has_many, aqui estamos tentando mapear Um cliente podendo fazer vários pedidos, porém para ter o “casamento” deste relacionamento é necessário criar o segundo schema, então crie um arquivo chamado order.ex com o conteúdo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defmodule PizzariaEcto.Order do
  use Ecto.Schema
  alias PizzariaEcto.{Client, Pizza, OrderPizza}

  schema "orders" do
    field :price, :float
    timestamps()

    belongs_to  :client, Client
    many_to_many :pizza, Pizza, join_through: OrderPizza
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No código acima fizemos a mesma coisa do código anterior, mapeamos determinados campos com uma tabela no banco de dados, entretanto temos a outra parte do primeiro relacionamento entre Cliente e Order, em que usamos a macro belongs_to,  com esse relacionamento estabelecido é esperado que tenha uma coluna na tabela orders chamada client_id.&lt;/p&gt;

&lt;h2&gt;
  
  
  Associação Muitos para Muitos
&lt;/h2&gt;

&lt;p&gt;Um outro relacionamento que precisamos mapear é entre Order e Pizza, como é uma associação muito para muitos é necessário a criação de uma terceira tabela que vai fazer o papel de guardar order_id e pizza_id, assim é necessário usar a macro many_to_many em ambos os schemas e usar a keyword join_through mostrando qual o schema será usado. Vamos criar o schema de pizza, então crie um arquivo chamado pizza.ex, com o conteúdo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defmodule PizzariaEcto.Pizza do
  use Ecto.Schema
  alias PizzariaEcto.{Order,OrderPizza}

  schema "pizzas" do
    field :name, :string
    timestamps()

    many_to_many :order, Order, join_through: OrderPizza
  end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vamos também criar o schema &lt;strong&gt;OrderPizza&lt;/strong&gt;, então crie o arquivo &lt;strong&gt;order_pizza.ex.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defmodule PizzariaEcto.OrderPizza do
  use Ecto.Schema
  alias PizzariaEcto.{Pizza, Order}

  schema "orders_pizzars" do
    timestamps()
    belongs_to  :order, Order
    belongs_to  :pizza, Pizza

  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ótimo, com esses schemas criados poderemos utiliza-los para fazer determinadas operações no banco de dados, mas com a criação desses ainda não estão criadas as tabelas no banco de dados, para isso é necessário criar as migrações, então vamos lá.&lt;/p&gt;

&lt;h2&gt;
  
  
  Criando migrations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Tabela de Cliente
&lt;/h3&gt;

&lt;p&gt;O Ecto se utiliza de um componente chamado Ecto.Migrations para fazer as migrações necessárias, então para cria-la podemos usar um linha de comando no terminal, então digite:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix ecto.gen.migrations add_clients_table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O código acima vai criar um arquivo dentro do seguinte path &lt;em&gt;&lt;strong&gt;priv/SEU_REPO/migrations/ID_ALEATÓRIO_add_clients_table&lt;/strong&gt;&lt;/em&gt; , como eu já mencionei podemos ter vários repositórios, então este SEU_REPO significa qual repositório será criado a migração, porém como existe apenas um, então será ele. O conteúdo do arquivo criado, será o seguinte:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defmodule PizzariaEcto.Repo.Migrations.AddClientsTable do
  use Ecto.Migration

  def change do
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esta função change será a chamada pelo Ecto para fazer a migração, então vamos adicionar a nossa tabela:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defmodule PizzariaEcto.Repo.Migrations.AddClientsTable do
  use Ecto.Migration

  def change do
    create table("clients") do
      add :name, :string, null: false
      add :phone, :string, null: false
      timestamps()
    end

    create index("clients", :name)
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Temos aqui a macro create que recebe o que será criado no banco e um bloco de código, então estamos criando a tabela clients com algumas colunas, podemos perceber que o nome aqui é o mesmo que colocamos no schema PizzariaEcto.Client. Além da tabela poderemos criar index para maximizar as buscas por uma coluna no banco. Podemos criar agora a tabela de Order.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tabela de Order
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix ecto.gen.migrations add_orders_table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Criado o arquivo &lt;em&gt;&lt;strong&gt;priv/SEU_REPO/migrations/ID_ALEATÓRIO_add_orders_table&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defmodule PizzariaEcto.Repo.Migrations.AddOrdersTable do
  use Ecto.Migration

  def change do
    create table("orders") do
      add :price, :float, null: false
      add :client_id, references("clients",  on_delete: :nothing)
      timestamps null: true
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Veja que pelo fato de termos uma associação de 1 para muitos entre Cliente e Order , então é necessário que orders tenha a &lt;strong&gt;foreign_key&lt;/strong&gt;, então podemos adicionar usando o tipo &lt;strong&gt;references&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tabela de Pizza
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix ecto.gen.migrations add_pizzas_table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defmodule PizzariaEcto.Repo.Migrations.AddPizzasTable do
  use Ecto.Migration

  def change do
    create table("pizzas") do
      add :name, :string, null: false

      timestamps null: true
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No código acima criamos a migration de pizza, mas podemos voltar no schema de Pizza e Order, e ver que eles tem uma relação de muitos para muitos entre eles, e criamos um schema OrderPizza para receber as foreign_keys, então vamos criar a tabela orders_pizzas que mapeamos no schema OrderPizza.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix ecto.gen.migrations add_orders_pizzas_table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defmodule PizzariaEcto.Repo.Migrations.AddOrdersPizzasTable do
  use Ecto.Migration

  def change do
    create table("orders_pizzas") do
      add :order_id, references("orders")
      add :pizza_id, references("pizzas")
      timestamps null: true
    end

    create index(:orders_pizzas, :order_id)
    create index(:orders_pizzas, :pizza_id)
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OK, todas as migrations foram criadas até agora, mas é necessário executar a migração, então vamos digitar no terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mix ecto.migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E done!&lt;/p&gt;

&lt;p&gt;Vamos para parte 2:&lt;br&gt;
&lt;a href="https://dev.to/wesleymorais/elixir-como-fazer-determinadas-operacoes-com-a-biblioteca-ecto-parte-2-2gbh"&gt;Segunda Parte do Artigo&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/ecto/Ecto.html" rel="noopener noreferrer"&gt;Ecto Site Oficial&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pragprog.com/titles/wmecto/programming-ecto/" rel="noopener noreferrer"&gt;Programming Ecto - Build Database Apps in Elixir for Scalability and Performance&lt;/a&gt;&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>softwaredevelopment</category>
      <category>ai</category>
      <category>career</category>
    </item>
  </channel>
</rss>
