<?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: Vinícius Ramos</title>
    <description>The latest articles on DEV Community by Vinícius Ramos (@viniciusramos91).</description>
    <link>https://dev.to/viniciusramos91</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1041421%2Ff4418b12-8d97-43d4-b4b6-e5d7c2358a1f.png</url>
      <title>DEV Community: Vinícius Ramos</title>
      <link>https://dev.to/viniciusramos91</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/viniciusramos91"/>
    <language>en</language>
    <item>
      <title>My build in public journey: creating an Ebook generator</title>
      <dc:creator>Vinícius Ramos</dc:creator>
      <pubDate>Fri, 17 Jul 2026 22:46:40 +0000</pubDate>
      <link>https://dev.to/viniciusramos91/my-build-in-public-journey-creating-an-ebook-generator-hh0</link>
      <guid>https://dev.to/viniciusramos91/my-build-in-public-journey-creating-an-ebook-generator-hh0</guid>
      <description>&lt;p&gt;I once spent three weekends writing a single ebook for my Python course. Last month, my product (&lt;a href="https://ebookr.ai" rel="noopener noreferrer"&gt;Ebookr.ai&lt;/a&gt;) generated a better one in about fifteen minutes, while I was making coffee. This is the story of the gap between those two sentences.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem that started everything
&lt;/h2&gt;

&lt;p&gt;I run &lt;a href="https://pythonacademy.com.br/jornada-python/" rel="noopener noreferrer"&gt;Jornada Python&lt;/a&gt;, a Python course for Brazilian devs. Ebooks are great assets for a course business: lead magnets, bonus material, complementary content. Students love them.&lt;/p&gt;

&lt;p&gt;The problem: I hated making them.&lt;/p&gt;

&lt;p&gt;Writing was slow. Formatting was slower. And I am the kind of person who notices when a heading is 2px off, which turns "quick formatting pass" into a lost evening.&lt;/p&gt;

&lt;p&gt;When ChatGPT showed up, I thought I was saved. Narrator voice: he was not. 🙃&lt;/p&gt;

&lt;p&gt;The text got faster, sure. But an ebook is not text. It is text plus a cover plus images plus infographics plus layout plus a PDF that does not break in hilarious ways. So my workflow became: generate text in one tab, make a cover in another tool, hunt stock images in a third, assemble everything in a design tool, export, find a table sliced in half between pages, cry, repeat. The result of all that gluing looked like a ransom note with chapters.&lt;/p&gt;

&lt;p&gt;That pain felt very automatable. So I built &lt;a href="https://ebookr.ai" rel="noopener noreferrer"&gt;Ebookr.ai&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;The core is a multi-agent pipeline. Instead of one giant prompt praying for a miracle, there are six specialized agents, each with one job:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an &lt;strong&gt;Architect&lt;/strong&gt; that turns an idea into a structured outline&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;Writer&lt;/strong&gt; that produces each section&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;Coder&lt;/strong&gt; for technical ebooks (code samples that actually run)&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;QA agent&lt;/strong&gt; that reviews content and can reject it with feedback&lt;/li&gt;
&lt;li&gt;an &lt;strong&gt;Infographic agent&lt;/strong&gt; that turns key points into visuals&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;Layout Designer&lt;/strong&gt; that assigns each page a template archetype&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The PDF part is where I lost some hair. My first version used Markdown to Pandoc to WeasyPrint, which works right up until you want magazine-style layouts. The current version renders Jinja2 templates in headless Chromium via Playwright, with a custom pagination engine deciding where pages break. Yes, I wrote a pagination engine in 2026. No, I do not want to talk about it. (I do. Ask me anything.)&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;p&gt;Boring on purpose: Django 5 and Python 3.11, Celery with Redis for all the heavy async work, Postgres with pgvector, LangChain and LangGraph orchestrating the agents, OpenAI models behind it, Playwright for PDF rendering, Stripe for billing, Cloudflare R2 for storage.&lt;/p&gt;

&lt;p&gt;The most exotic thing here is the agent orchestration. Everything else is the stack you would pick for a normal SaaS, because 90% of an AI product is a normal SaaS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things that broke (a selection)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chromium ate my server.&lt;/strong&gt; Concurrent PDF renders of 100-page ebooks with embedded images took down the web workers via the kernel OOM killer. Fix: a dedicated Celery queue with concurrency of exactly 1 and a hard memory limit. Humbling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Walking tables.&lt;/strong&gt; Tables that straddled a page break would either vanish or duplicate. The pagination engine now knows how to split tables, lists and quotes across pages like a civilized adult.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The preview that shared too much.&lt;/strong&gt; My free preview was quietly mounting the entire ebook client-side, including the paid chapters. Found it, fixed it in four layers, wrote a tripwire test so it can never come back. Build in public also means confessing in public.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Editor performance.&lt;/strong&gt; A 70+ page ebook made the in-browser editor melt. Profiling sessions brought CPU time from 12s to under 5s. The culprit was a decorative noise texture being regenerated per page. Design has a price.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Claude as my AI companion developer
&lt;/h2&gt;

&lt;p&gt;I build almost everything using Claude Code sessions. Not as an autocomplete, more like a colleague with unlimited patience: it reads the codebase, follows the project's rule file, writes the tests, and pushes back when I ask for something that contradicts a business rule we defined earlier. Being corrected by your own tooling is a strange kind of pride.&lt;/p&gt;

&lt;p&gt;The workflow that emerged: I describe intent and constraints, Claude proposes and implements, tests gate everything, and I review like a slightly paranoid tech lead. Solo founder, but it rarely feels solo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then I gave it a second job: marketing manager
&lt;/h2&gt;

&lt;p&gt;This is my favorite part. I am a developer; ads dashboards scare me more than segfaults. So I set up a dedicated marketing agent: its own instructions file, its own subagents (data analyst, CRO specialist, copywriter), and read-only access to everything that matters: Google Ads, GA4, Search Console, Microsoft Clarity and Stripe through MCP connectors, plus the production database through an SSH tunnel with a read-only role.&lt;/p&gt;

&lt;p&gt;Every day it cross-checks ad spend against real signups and real revenue (never trusting the ad platform's own numbers), and ends every report with three prioritized actions, what NOT to do, and what data is missing.&lt;/p&gt;

&lt;p&gt;Why an agent instead of an agency? Because I hired an agency first. What the agent found while auditing my accounts deserves its own horror anthology 🕵️:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;their campaign report celebrated "132 trial starts". The product has never had a trial. The event fired when someone merely visited the signup page.&lt;/li&gt;
&lt;li&gt;the real number for that same campaign, straight from my database: 1 subscription for about 115 dollars spent.&lt;/li&gt;
&lt;li&gt;my Meta Business Manager contained assets from a completely different client of theirs, receiving live events.&lt;/li&gt;
&lt;li&gt;and my own site was carrying a Meta pixel that belonged to no account I own, quietly shipping my visitors' data somewhere I could not see.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent surfaced all of this in one afternoon of cross-checking, then we rebuilt the tracking from scratch. The agency is gone. The agent stays.&lt;/p&gt;

&lt;p&gt;Guardrails matter: it can read everything, it can change nothing. Every mutation goes through me. An autonomous agent with write access to your ad account is a horror movie pitch.&lt;/p&gt;

&lt;h2&gt;
  
  
  First numbers, honestly
&lt;/h2&gt;

&lt;p&gt;Build in public without numbers is just marketing, so here goes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10 paying subscribers, about 120 dollars in MRR (it's charged in BRL - for now. i18n in development)&lt;/li&gt;
&lt;li&gt;retention is the bright spot: 2 cancellations ever, most subscribers renew&lt;/li&gt;
&lt;li&gt;organic visitors convert to paid at around 11%; cold paid traffic so far: under 1%, and I am publicly fighting that funnel right now&lt;/li&gt;
&lt;li&gt;cost per signup from search ads: about 60 cents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tiny numbers. But they are mine, they are real, and the unit economics work. The current battle is conversion, not the product, and that battle is being fought with session recordings, funnel instrumentation and a lot of stubbornness.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;The product is Portuguese-only today; the English version is fully built and waiting behind a feature flag while I finish the boring parts (legal review, USD pricing). If you read this far and want to see it anyway: &lt;a href="https://ebookr.ai" rel="noopener noreferrer"&gt;ebookr.ai&lt;/a&gt;. The pricing page is in Portuguese, but numbers are numbers. 😄&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>ai</category>
      <category>django</category>
      <category>python</category>
    </item>
    <item>
      <title>Como criar Middlewares customizados no Django</title>
      <dc:creator>Vinícius Ramos</dc:creator>
      <pubDate>Wed, 08 Mar 2023 21:58:22 +0000</pubDate>
      <link>https://dev.to/viniciusramos91/como-criar-middlewares-customizados-no-django-219m</link>
      <guid>https://dev.to/viniciusramos91/como-criar-middlewares-customizados-no-django-219m</guid>
      <description>&lt;p&gt;Fala &lt;strong&gt;Dev&lt;/strong&gt;! Na paz?!&lt;/p&gt;

&lt;p&gt;Nesse meu primeiro post aqui na Dev.to você vai aprender a criar &lt;em&gt;Middlewares&lt;/em&gt; no Django, que são trechos de código para tratamento prévio ou posterior de requisições que chegam e saem do seu sistema Django.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Ficou confuso? Então bora nessa!&lt;/em&gt; 😉&lt;/p&gt;

&lt;h2&gt;
  
  
  O que são &lt;em&gt;Middlewares&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Middlewares&lt;/em&gt; vivem na &lt;a href="https://pythonacademy.com.br/blog/desenvolvimento-web-com-python-e-django-view"&gt;Camada View&lt;/a&gt; do framework Web Django (que também é composto pela &lt;a href="https://pythonacademy.com.br/blog/desenvolvimento-web-com-python-e-django-model"&gt;Camada Model&lt;/a&gt; e pela &lt;a href="https://pythonacademy.com.br/blog/desenvolvimento-web-com-python-e-django-template"&gt;Camada Template&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Middlewares&lt;/em&gt; são trechos de códigos que podem ser executados antes ou depois do processamento de requisições/respostas pelo Django.&lt;/p&gt;

&lt;p&gt;É uma forma que os desenvolvedores, nós, temos para alterar como o Django processa algum dado de entrada ou de saída.&lt;/p&gt;

&lt;p&gt;Se você olhar no arquivo &lt;code&gt;settings.py&lt;/code&gt;, nós temos a &lt;a href="https://pythonacademy.com.br/blog/listas-no-python"&gt;lista&lt;/a&gt; &lt;code&gt;MIDDLEWARE&lt;/code&gt; com diversos &lt;em&gt;middlewares&lt;/em&gt; pré-configurados:&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;MIDDLEWARE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s"&gt;'django.middleware.security.SecurityMiddleware'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'django.contrib.sessions.middleware.SessionMiddleware'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'django.middleware.common.CommonMiddleware'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'django.middleware.csrf.CsrfViewMiddleware'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'django.contrib.auth.middleware.AuthenticationMiddleware'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'django.contrib.messages.middleware.MessageMiddleware'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;'django.middleware.clickjacking.XFrameOptionsMiddleware'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por exemplo, o &lt;em&gt;middleware&lt;/em&gt; &lt;code&gt;AuthenticationMiddleware&lt;/code&gt; é reponsável por adicionar a variável &lt;code&gt;user&lt;/code&gt; a todas as requisições.&lt;/p&gt;

&lt;p&gt;Dessa forma, você pode, por exemplo, mostrar o usuário logado no seu &lt;em&gt;template&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/alguma/url"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Olá, {{ user.email }}&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vamos ver agora o ciclo de vida de um &lt;em&gt;Middleware&lt;/em&gt; dentro do Django.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ciclo de vida de um &lt;em&gt;Middleware&lt;/em&gt; no Django
&lt;/h2&gt;

&lt;p&gt;Um &lt;em&gt;middleware&lt;/em&gt; é um método &lt;em&gt;callable&lt;/em&gt; (que implementa o método &lt;code&gt;__call__()&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Esse método recebe uma &lt;strong&gt;requisição&lt;/strong&gt; e retorna uma &lt;strong&gt;resposta&lt;/strong&gt;, assim como uma &lt;em&gt;View&lt;/em&gt;, podendo ser escrito como função ou como Classe.&lt;/p&gt;

&lt;p&gt;Um exemplo de &lt;em&gt;middleware&lt;/em&gt; escrito como função é:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;middleware_simples&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get_response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Código de inicialização do Middleware
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Aqui vai o código a ser executado antes 
&lt;/span&gt;        &lt;span class="c1"&gt;# da View e de outros middlewares
&lt;/span&gt;        &lt;span class="c1"&gt;# vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
&lt;/span&gt;        &lt;span class="c1"&gt;# Aqui vai o código a ser executado 
&lt;/span&gt;        &lt;span class="c1"&gt;# para cada resposta após a View 
&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;middleware&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E como Classe:&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MiddlewareSimples&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_response&lt;/span&gt;
        &lt;span class="c1"&gt;# Código de inicialização do Middleware
&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__call__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Aqui vai o código a ser executado antes 
&lt;/span&gt;        &lt;span class="c1"&gt;# da View e de outros middlewares
&lt;/span&gt;        &lt;span class="c1"&gt;# vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
&lt;/span&gt;        &lt;span class="c1"&gt;# Aqui vai o código a ser executado 
&lt;/span&gt;        &lt;span class="c1"&gt;# para cada resposta após a View         
&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como cada &lt;em&gt;Middleware&lt;/em&gt; é executado de maneira encadeada, do topo da configuração &lt;code&gt;MIDDLEWARE&lt;/code&gt; (&lt;em&gt;do settings.py&lt;/em&gt;) para o fim, a saída de um é a entrada do próximo.&lt;/p&gt;

&lt;p&gt;O método &lt;code&gt;get_response()&lt;/code&gt; pode ser a própria &lt;em&gt;View&lt;/em&gt;, caso ela seja a última configurada no &lt;code&gt;MIDDLEWARE&lt;/code&gt; do &lt;em&gt;settings.py&lt;/em&gt;, ou o próximo &lt;em&gt;middleware&lt;/em&gt; da cadeia.&lt;/p&gt;

&lt;p&gt;Utilizando a construção do &lt;em&gt;middleware&lt;/em&gt; via Classe, nós temos três métodos importantes: &lt;/p&gt;

&lt;h3&gt;
  
  
  O método &lt;code&gt;process_view&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;👉 &lt;strong&gt;Assinatura&lt;/strong&gt;: &lt;code&gt;process_view(request, func, args, kwargs)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Esse método é chamado logo antes do Django executar a &lt;em&gt;View&lt;/em&gt; que vai processar a requisição e possui os seguintes parâmetros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;request&lt;/code&gt; é o objeto &lt;code&gt;HttpRequest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;func&lt;/code&gt; é a própria &lt;em&gt;view&lt;/em&gt; que o Django está para chamar ao final da cadeia de &lt;em&gt;middlewares&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pythonacademy.com.br/blog/args-e-kwargs-do-python"&gt;&lt;code&gt;args&lt;/code&gt;&lt;/a&gt; é a lista de parâmetros posicionais que serão passados à &lt;em&gt;view&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pythonacademy.com.br/blog/args-e-kwargs-do-python"&gt;&lt;code&gt;kwargs&lt;/code&gt;&lt;/a&gt; é o &lt;em&gt;dict&lt;/em&gt; contendo os argumentos nomeados (&lt;em&gt;keyword arguments&lt;/em&gt;) que serão passados à &lt;em&gt;view&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esse método deve retornar &lt;code&gt;None&lt;/code&gt; ou um objeto &lt;code&gt;HttpResponse&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Caso retorne &lt;code&gt;None&lt;/code&gt;, o Django entenderá que deve continuar a cadeia de &lt;em&gt;Middlewares&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Caso retorne &lt;code&gt;HttpResponse&lt;/code&gt;, o Django entenderá que a resposta está pronta para ser enviada de volta e não vai se preocupar em chamar o resto da cadeia de &lt;em&gt;Middlewares&lt;/em&gt;, nem a &lt;em&gt;view&lt;/em&gt; que iria processar a requisição.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  O método &lt;code&gt;process_exception&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;👉 &lt;strong&gt;Assinatura&lt;/strong&gt;: &lt;code&gt;process_exception(request, exception)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Esse método é chamada quando uma &lt;em&gt;view&lt;/em&gt; lança uma exceção e deve retornar ou &lt;code&gt;None&lt;/code&gt; ou &lt;code&gt;HttpResponse&lt;/code&gt;. Caso retorne um objeto &lt;code&gt;HttpResponse&lt;/code&gt;, o Django irá aplicar o &lt;em&gt;middleware&lt;/em&gt; de resposta e o de &lt;em&gt;template&lt;/em&gt;, retornando a requisição ao &lt;em&gt;browser&lt;/em&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;request&lt;/code&gt; é o objeto &lt;code&gt;HttpRequest&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;exception&lt;/code&gt; é a exceção propriamente dita lançada pela &lt;em&gt;view&lt;/em&gt; (&lt;code&gt;Exception&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  O método &lt;code&gt;process_template_response&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;👉 &lt;strong&gt;Assinatura&lt;/strong&gt;: &lt;code&gt;process_template_response(request, response)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Esse método é chamado logo após a &lt;em&gt;view&lt;/em&gt; ter terminado sua execução, caso a resposta tenha uma chamada ao método &lt;code&gt;render()&lt;/code&gt; indicando que a reposta possui um &lt;em&gt;template&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Possui os seguintes parâmetros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;request&lt;/code&gt; é um objeto &lt;code&gt;HttpRequest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;response&lt;/code&gt; é o objeto &lt;code&gt;TemplateResponse&lt;/code&gt; retornado pela &lt;em&gt;view&lt;/em&gt; ou por outro &lt;em&gt;middleware&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Agora vamos criar um &lt;em&gt;middleware&lt;/em&gt; um pouco mais complexo para exemplificar o que foi dito aqui!&lt;/p&gt;

&lt;h2&gt;
  
  
  Criando um &lt;em&gt;Middleware&lt;/em&gt; no Django
&lt;/h2&gt;

&lt;p&gt;Vamos supor que queremos um &lt;em&gt;middleware&lt;/em&gt; que filtre requisições e só processe aquelas que venham de uma determinada lista de IP's.&lt;/p&gt;

&lt;p&gt;O que precisamos fazer é abrir o cabeçalho de todas as requisições que chegam no nosso servidor e verificar se o IP de origem bate com a nossa lista de IP's.&lt;/p&gt;

&lt;p&gt;Para isso, colocamos a lógica no método &lt;code&gt;process_view&lt;/code&gt;, da seguinte forma:&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FiltraIPMiddleware&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_response&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_response&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__call__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Lista de IPs autorizados
&lt;/span&gt;    &lt;span class="n"&gt;ips_autorizados&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'127.0.0.1'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# IP do usuário
&lt;/span&gt;    &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;META&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'REMOTE_ADDR'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Verifica se o IP do cliente está na lista de IPs autorizados
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ip&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ips_autorizados&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Se usuário não autorizado &amp;gt; HTTP 403: Não Autorizado
&lt;/span&gt;      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HttpResponseForbidden&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"IP não autorizado"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Se for autorizado, não fazemos nada
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depois disso, precisamos registrar nosso &lt;em&gt;middleware&lt;/em&gt; no arquivo de configurações &lt;em&gt;settings.py&lt;/em&gt; (na configuração &lt;code&gt;MIDDLEWARE&lt;/code&gt;):&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;MIDDLEWARE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="c1"&gt;# Middlewares do próprio Django
&lt;/span&gt;  &lt;span class="s"&gt;'django.middleware.security.SecurityMiddleware'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;'django.contrib.sessions.middleware.SessionMiddleware'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;'django.middleware.common.CommonMiddleware'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;'django.middleware.csrf.CsrfViewMiddleware'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;'django.contrib.auth.middleware.AuthenticationMiddleware'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;'django.contrib.messages.middleware.MessageMiddleware'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;'django.middleware.clickjacking.XFrameOptionsMiddleware'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="c1"&gt;# Nosso Middleware
&lt;/span&gt;  &lt;span class="s"&gt;'helloworld.middlewares.FiltraIPMiddleware'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora, podemos testar seu funcionamento alterando a lista &lt;code&gt;ips_autorizados&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Coloque &lt;code&gt;ips_autorizados = ['127.0.0.1']&lt;/code&gt; e tente acessar alguma URL da nossa aplicação: devemos conseguir acessar normalmente nossa aplicação, pois como estamos executando o servidor localmente, nosso IP será 127.0.0.1 e, portanto, passaremos no teste.&lt;/li&gt;
&lt;li&gt;Coloque &lt;code&gt;ips_autorizados = []&lt;/code&gt; e tente acessar alguma URL da nossa aplicação: deve aparecer a mensagem de &lt;strong&gt;"IP não autorizado"&lt;/strong&gt;, pois nosso IP (127.0.0.1) não está autorizado a  acessar o servidor.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Nesse primeiro &lt;em&gt;post&lt;/em&gt; quis trazer como é simples criar &lt;em&gt;Middlewares&lt;/em&gt; no Django.&lt;/p&gt;

&lt;p&gt;Você viu como podemos adicionar tratamento prévio ao processamento de requisições e posterior ao processamento de respostas.&lt;/p&gt;

&lt;p&gt;Seus casos de uso são bem específicos, contudo é bom saber que temos essa possibilidade!&lt;/p&gt;

&lt;p&gt;É isso dev, nos vemos na próxima! 😉&lt;/p&gt;

</description>
      <category>python</category>
      <category>django</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
